Python并发编程之多线程

Posted cs_1993

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Python并发编程之多线程相关的知识,希望对你有一定的参考价值。

本节内容

  • 什么是线程
  • 线程与进程的区别
  • 开启线程的两种方式
  • Thread对象的其他属性或方法
  • 守护线程
  • GIL全局解释器锁
  • 死锁和递归锁
  • 信号量 event 计时器
  •  线程queue

一 什么是线程

线程相对于进程更为轻量级,当一个进程启动同时也会启动一个主线程,多线程就是指在一个进程下创建多个线程并且这些线程共享地址空间。所以进程只是用来把资源集中到一起(进程只是一个资源单位,或者说资源集合),而线程才是cpu上的执行单位。

二 线程与进程的区别

1 Threads share the address space of the process that created it; processes have their own address space.

2 Threads have direct access to the data segment of its process; processes have their own copy of the data segment of the parent process.

3 Threads can directly communicate with other threads of its process; processes must use interprocess communication to communicate with sibling processes.

4 New threads are easily created; new processes require duplication of the parent process.

5 Threads can exercise considerable control over threads of the same process; processes can only exercise control over child processes.

6 Changes to the main thread (cancellation, priority change, etc.) may affect the behavior of the other threads of the process; changes to the parent process does not affect child processes.

总结上述区别,无非两个关键点,这也是我们在特定的场景下需要使用多线程的原因:

同一个进程内的多个线程共享该进程内的地址资源
创建线程的开销要远小于创建进程的开销(创建一个进程,就是创建一个车间,涉及到申请空间,而且在该空间内建至少一条流水线,但创建线程,就只是在一个车间内造一条流水线,无需申请空间,所以创建开销小)

三 开启线程的两种方式

开启线程的方式

方式一
from threading import Thread
import time

def sayhi(name):
    time.sleep(2)
    print(%s say hello %name)

 if __name__ == __main__:
    t=Thread(target=sayhi,args=(harry,))
    t.start()
    print(主线程)


方式二
from threading import Thread
import time

class Sayhi(Thread):
    def __init__(self,name):
         super().__init__()
         self.name=name
         def run(self):
         time.sleep(2)
         print(%s say hello % self.name)

if __name__ == __main__:
       t = Sayhi(harry)
       t.start()

基于多进程多线程实现套接字通信

技术分享图片
 1 import socket
 2 from multiprocessing import Process
 3 from threading import Thread
 4 
 5 def create_socket():
 6      server = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
 7      server.bind((127.0.0.1, 8080))
 8      server.listen(5)
 9      return server
10 
11 def talk(conn):
12 
13     while True:
14     try:
15         data = conn.recv(1024)
16         if data is None:break
17         conn.send(data.upper())
18     except ConnectionError:
19         break
20         conn.close()
21 
22 def communication(server):
23 
24     while True:
25         conn,add = server.accept()
26         t = Thread(target=talk, args=(conn,))
27             t.start()
28 
29 if __name__ == __main__:
30      server = create_socket()
31      p1 = Process(target=communication, args=(server,))
32      p2 = Process(target=communication, args=(server,))
33      p1.start()
34      p2.start() 
View Code

编写一个简单的文本处理工具,具备三个任务,一个接收用户输入,一个将用户输入的内容格式化成大写,一个将格式化后的结果存入文件

技术分享图片
 1 from threading import  Thread
 2     msg_l = []
 3         format_l = []
 4 
 5 def user_input():
 6     while True:
 7         text = input(请输入内容:)
 8         if text is None:continue
 9         msg_l.append(text)
10 
11 
12 def format_text():
13     while True:
14         if msg_l:
15         reg = msg_l.pop()
16         format_l.append(reg.upper())
17 
18 def save():
19     while True:
20         if format_l:
21         with open(db1.txt,a,encoding=utf-8) as f:
22         res = format_l.pop()
23         f.write(%s\n %res)
24         f.flush()
25 
26 if __name__ == __main__:
27             
28     t1 = Thread(target=user_input)
29         t2 = Thread(target=format_text)
30     t3 = Thread(target=save)
31 
32     t1.start()
33     t2.start()
34     t3.start()
View Code

 

四 Thread对象的其他属性或方法

Thread实例对象的方法
      # isAlive(): 返回线程是否活动的。 
      # getName(): 返回线程名。
      # setName(): 设置线程名。
threading模块提供的一些方法: # threading.currentThread(): 返回当前的线程变量。 # threading.enumerate(): 返回一个包含正在运行的线程的list。正在运行指线程启动后、结束前,不包括启动前和终止后的线程。 # threading.activeCount(): 返回正在运行的线程数量,与len(threading.enumerate())有相同的结果。

五 守护线程

无论是进程还是线程,都遵循:守护xxx会等待主xxx运行完毕后被销毁
需要强调的是:运行完毕并非终止运行
1、对主进程来说,运行完毕指的是主进程代码运行完毕
2、对主线程来说,运行完毕指的是主线程所在的进程内所有非守护线程统统运行完毕,主线程才算运行完毕

详细解释:

1、主进程在其代码结束后就已经算运行完毕了(守护进程在此时就被回收),然后主进程会一直等非守护的子进程都运行完毕后回收子进程的资源(否则会产生僵尸进程),才会结束,
2、主线程在其他非守护线程运行完毕后才算运行完毕(守护线程在此时就被回收)。因为主线程的结束意味着进程的结束,进程整体的资源都将被回收,而进程必须保证非守护线程都运行完毕后才能结束。

 

思考下述代码的执行结果有可能是哪些情况?为什么?

from threading import Thread
import time

def foo():
    print(123)
    time.sleep(1)
        print("end123")

def bar():
    print(456)
    time.sleep(3)
    print("end456")

if __name__ == __main__:
    t1=Thread(target=foo)
    t2=Thread(target=bar)

    t1.daemon=True
    t1.start()
    t2.start()
        print("main-------")
以上代码首先会输出 123,456,main, 随后会输出end123,end456。因为t1守护的是主进程,让主进程执行完print("main-------")线程2已经在运行了所以主进程并没有结束,等到子线程运行完毕才会回收子进程的资源进程才会结束

 








以上是关于Python并发编程之多线程的主要内容,如果未能解决你的问题,请参考以下文章

python并发编程之多线程

python并发编程之多线程基础知识点

Python并发编程之多线程

python并发编程之多线程编程

python并发编程之多线程守护系列互斥锁生产者消费者模型

python并发编程之多线程