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)

 

posted @ 2017-07-20 17:12  neuropathy_ldsly  阅读(581)  评论(0)    收藏  举报