Python线程进程[通信,Pipe,Manager,greenlet,gevent]
# 进程之间的通信 #Queue: import multiprocessing #导入模块 def foo(q): #接收队列对象 q.put([1111,"hello",True]) #向队列中添加一个值(列表形式) if __name__ == '__main__': q = multiprocessing.Queue() #创建进程队列 print(q) p = multiprocessing.Process(target=foo,args=(q,)) #把队列对象传入子进程中 p.start() print(q.get()) #从队列中获取数据 # 执行结果 C:\Users\Gldsly\AppData\Local\Programs\Python\Python36\python.exe E:/Python/DAY32/day32.py <multiprocessing.queues.Queue object at 0x000001AE5970CB00> [1111, 'hello', True] Process finished with exit code 0 # 管道 Pipe: from multiprocessing import Pipe,Process #导入Pipe def foo(sk): sk.send("Hello") #通过管道发送一个数据 if __name__ == '__main__': sock,conn = Pipe() #创建通道,返回两个连接对象。multiprocessing.Pipe([duplex])返回2个连接对象(conn1, conn2),代表管道的两端,默认是双向通信.如果duplex=False,conn1只能用来接收消息,conn2只能用来发送消息。 p = Process(target=foo,args=(sock,)) #传入连接对象到子进程中 p.start() print(conn.recv()) #接收并打印子进程发送的数据 #执行结果: C:\Users\Gldsly\AppData\Local\Programs\Python\Python36\python.exe E:/Python/DAY32/day32.py Hello Process finished with exit code 0 # 进程之间的数据共享 #Manager:
不同于Pipe和Queue,manager管理的资源可以让进城互相修改。
from multiprocessing import Manager,Process #导入Manager def foo(l,i): l.append(i**2) #计算数值并添加到资源中 if __name__ == '__main__': manager =Manager() #生成manager对象 mlist = manager.list([11,22,33]) #创建 manager列表,并初始赋予一些值 l = [] #初始建立一个列表用于子进程阻塞主进程,等待子进程操作完毕 for i in range(5): #循环五次开启五个子进程 p = Process(target=foo,args=(mlist,i)) #传入manager.list p.start() l.append(p) #添加子进程对象到列表中 for i in l: #循环列表进行阻塞 i.join() print(mlist) #等待子进程都操作完毕后 打印manager.list资源数值 # 执行结果: C:\Users\Gldsly\AppData\Local\Programs\Python\Python36\python.exe E:/Python/DAY32/day32.py [11, 22, 33, 4, 9, 1, 16, 0] Process finished with exit code 0
#进程池:
进程池内部维护一个进程序列,当使用时,则去进程池中获取一个进程,如果进程池序列中没有可供使用的进进程,那么程序就会等待,直到进程池中有可用进程为止。
例如:
下列代码:共有任务四个,但是进程池中只能两个进程同时跑,结果是数字两个一组的被打印出来
from multiprocessing import Pool
import time
def foo(args): #打印接收到的数值
time.sleep(1)
print(args)
if __name__ == '__main__':
p = Pool(2) #限定进程池中有同时只开两个进程
for i in range(4): #有任务四个
p.apply_async(func=foo, args= (i,)) #调用进程池去执行任务,并传入参数
p.close() # 等子进程执行完毕后关闭线程池
# time.sleep(2)
# p.terminate() # 立刻关闭线程池
p.join() #阻塞主进程
执行结果:
C:\Users\Gldsly\AppData\Local\Programs\Python\Python36\python.exe E:/Python/DAY32/day32.py
0
1
2
3
Process finished with exit code
# 协程
和多线程比,协程有何优势? 最大的优势就是协程极高的执行效率。因为子程序切换不是线程切换,而是由程序自身控制,因此,没有线程切换的开销,和多线程比,线程数量越多,协程的性能优势就越明显。 第二大优势就是不需要多线程的锁机制,因为只有一个线程,也不存在同时写变量冲突,在协程中控制共享资源不加锁,只需要判断状态就好了,所以执行效率比多线程高很多。 示例: import time def consumer(): r = '' #初始设置一个变量为空 while True: n = yield r #第一次next后会停在这里 if not n: return print('[CONSUMER] Consuming %s...' % n) #打印信息 time.sleep(1) #暂停一秒 r = '200 OK' #给r赋值200 def produce(c): next(c) #先给c初始化一下 n = 0 while n < 5: n = n + 1 print('[PRODUCER] Producing %s...' % n) #打印信息 r = c.send(n) #发送n到 consumer中并 next了一下 consumer print('[PRODUCER] Consumer return: %s' % r) #打印consumer返回信息 c.close() if __name__=='__main__': c = consumer() #获取consumer 赋值给c produce(c) #执行 produce函数 执行结果: [PRODUCER] Producing 1... [CONSUMER] Consuming 1... [PRODUCER] Consumer return: 200 OK [PRODUCER] Producing 2... [CONSUMER] Consuming 2... [PRODUCER] Consumer return: 200 OK [PRODUCER] Producing 3... [CONSUMER] Consuming 3... [PRODUCER] Consumer return: 200 OK [PRODUCER] Producing 4... [CONSUMER] Consuming 4... [PRODUCER] Consumer return: 200 OK [PRODUCER] Producing 5... [CONSUMER] Consuming 5... [PRODUCER] Consumer return: 200 OK
传统的生产者-消费者模型是一个线程写消息,一个线程取消息,通过锁机制控制队列和等待,但一不小心就可能死锁。 如果改用协程,生产者生产消息后,直接通过yield跳转到消费者开始执行,待消费者执行完毕后,切换回生产者继续生产。
#通过模块来实现yield的过程
greenlet模块: from greenlet import greenlet def foo(): print('Ok_1') gr2.switch() #当foo函数执行到这一步的时候会通过这条语句跳到 bar函数继续执行 print('ok_3') gr2.switch() def bar(): print('Ok_2') gr1.switch() #当bar函数执行到这里后,会跳回foo函数 print('ok_4') gr1 = greenlet(foo) #定义greenlet对象 gr2 = greenlet(bar) gr1.switch() # 执行结果: C:\Users\Gldsly\AppData\Local\Programs\Python\Python36\python.exe E:/Python/DAY32/day32.py Ok_1 Ok_2 ok_3 ok_4 Process finished with exit code 0 #gevent:
另一种协程方式
from gevent import monkey #对gevent模块进行优化,必须写在 gevent之前。没优化之前 当gevent模块遇到I/O阻塞时并不会自动切换 monkey.patch_all() import requests,gevent,time def foo(url): respnse=requests.get(url) respnse_str=respnse.text print("GET data %s --url:%s"%(len(respnse_str),url)) s=time.time() gevent.joinall([gevent.spawn(foo,"https://itk.org/"), gevent.spawn(foo, "https://www.github.com/"), gevent.spawn(foo, "http://tieba.baidu.com/"), gevent.spawn(foo, "http://www.cnblogs.com/ldsly"), gevent.spawn(foo, "http://www.xiaopian.com/"), gevent.spawn(foo, "https://www.bilibili.com/")]) #<-----耗时:2.9957852363586426 foo("https://itk.org/") foo("https://www.github.com/") foo("http://tieba.baidu.com/") foo("http://www.cnblogs.com/ldsly") foo("http://www.xiaopian.com/") foo("https://www.bilibili.com/") #<------耗时:7.497825860977173 print(time.time()-s)
浙公网安备 33010602011771号