Python并发编程基础 №④ 管道、数据共享、进程池(原理、效率测试、回调函数)

1、进程间数据传递之管道(Pipe)

 1 conn1, conn2 = Pipe()
 2 conn1.send('hello Pipe')
 3 print(conn2.recv())
 4 conn1.close()
 5 # print(conn2.recv())  # EOFError
 6 conn2.close()
 7 
 8 # demo2
 9 def recv(conn):
10     while 1:
11         re = conn.recv()
12         if re:
13             print(re)
14         else:
15             break
16     conn.close()
17 
18 if __name__ == '__main__':
19     conn1, conn2 = Pipe()
20     Process(target=recv, args=(conn2,)).start()
21     for i in range(3):
22         conn1.send('hello recv%d'%i )
23     conn1.send(None)
24     conn1.close()
25     conn2.close()
26 
27 # demo3
28 def get(conn1, conn2):
29     conn1.close()
30 
31     while 1:
32         try:
33             print('========', conn2.recv())
34         except EOFError:
35             conn2.close()
36             break
37 
38 if __name__ == "__main__":
39     conn1, conn2 = Pipe()
40     Process(target=get, args=(conn1, conn2)).start()
41     conn2.close()
42     for i in range(5):
43         conn1.send('hello main')
44     conn1.close()
View Code

2、进程间的数据共享 Manager

 1 from multiprocessing import Manager
 2 from multiprocessing import Process
 3 
 4 
 5 def add(dic):
 6     print(dic['count'])  # 子进程中可以使用
 7     dic['count'] += 1
 8 
 9 
10 if __name__ == '__main__':
11 
12     manager = Manager()
13     dic = manager.dict({'count': 100})  # 主进程定义的dict
14     # dic.setdefault('name', 'tom')  # todo 不能如此增加字典中的项
15 
16     p_lst = []
17     for i in range(10):
18         p = Process(target=add, args=(dic, ))
19         p.start()
20         p_lst.append(p)
21     [p.join() for p in p_lst]
22     print(dic)
23     #  p.join()  # 没有join会报错:FileNotFoundError: [WinError 2] 系统找不到指定的文件。
View Code

3、用管道来实现生产者和消费者问题

 1 import time, random
 2 from multiprocessing import Process
 3 from multiprocessing import Pipe
 4 
 5 
 6 def produce(conn1, conn2):
 7     conn2.close()
 8     for i in range(10):
 9         time.sleep(random.random())
10         msg = '\33[32m新闻社刚刚生成了一条消息,编号No:%d\33[0m' % i
11         print(msg)
12         conn1.send(msg)
13     conn1.close()
14 
15 
16 def consume(conn):
17     while 1:
18         try:
19             print('消费者消费了:', conn.recv())
20         except EOFError:
21             conn.close()
22             break
23 
24 
25 if __name__ == '__main__':
26     conn1, conn2 = Pipe()
27 
28     p1 = Process(target=produce, args=(conn1, conn2))
29     p1.start()
30     conn1.close()
31 
32     c = Process(target=consume, args=(conn2,))
33     time.sleep(random.randint(1,2))
34     c.start()
35 
36     conn2.close()
View Code

4、进程池

(1)原理

  为什么会有进程池的概念
    效率
    每开启进程,开启属于这个进程的内存空间
  寄存器 堆栈 文件
  进程过多 操作系统的调度

  进程池
  python中的 先创建一个属于进程的池子
  这个池子指定能存放n个进程
  先将这些进程创建好

(2)demo与进程池效率测试

 1 from multiprocessing import Pool, Process
 2 
 3 # demo1:
 4 def func(x):
 5     time.sleep(random.random())
 6     print('hello ***')
 7     print('-'*46)
 8 
 9 
10 if __name__ == '__main__':
11     pool = Pool(7)  # 此处可不设,设的话就设你的cpu核数加1,即一次起7个进程
12     pool.map(func, range(21))

 1 # demo2 map方法
 2 # 与传统的Process比较,Pool明显效率更高!
 3 def func(n):
 4     for i in range(10):
 5         print(n+1)
 6 
 7 def func2(n):
 8     for i in range(10):
 9         print(n+2)
10 
11 if __name__ == '__main__':
12     start = time.time()
13     pool = Pool(7)               # 7个进程
14     pool.map(func, range(100))    # 100个任务
15     #pool.map(func2, [('tom', 1), 'jack'])  # map的传多个参数
16 
17     t1 = time.time() - start  # 耗时 0.26628756523132324
18 
19     start = time.time()
20     p_lst = []
21     for i in range(100):  # 100个任务
22         p = Process(target=func, args=(i,))
23         p_lst.append(p)
24         p.start()
25     for p in p_lst:p.join()
26     t2 = time.time() - start  # 耗时1.8825409412384033
27 
28     print(t1, t2)  # 0.26628756523132324 1.8825409412384033

View Code

 

(3) apply 与apply_asyn方法

 1 def show(n):
 2     print(n*n)
 3 
 4 if __name__ == '__main__':
 5     pool = Pool()
 6     for i in range(10):
 7         #pool.apply(show, args=(i,))  # 同步,不需要下面的代码,也会输出结果!
 8         pool.apply_async(show, args=(i, ))  # 需要下面的代码,才能有输出!
 9     pool.close()  # 结束进程池接收任务
10     pool.join()   # 感知进程池中的任务执行结束

 

(4)进程池的返回值

 1 from multiprocessing import Pool
 2 
 3 
 4 def func(num):
 5     return num*num
 6 
 7 # demo1 : 使用apply_async,得到返回记过,然后get,就变同步计算了!
 8 if __name__ == '__main__':
 9     pool = Pool(7)
10     for i in range(10):
11         res = pool.apply_async(func, args=(i,))
12         #print(res)  # <multiprocessing.pool.ApplyResult object at 0x0000022474085E10>
13         print(res.get())  # 同步显示
14 
15 
16 # demo2:解决,异步计算,同步取结果
17 if __name__ == '__main__':
18     pool = Pool(7)
19     res_lst = []
20     for i in range(10):
21         res = pool.apply_async(func, args=(i, ))
22         res_lst.append(res)
23     print([res.get() for res in res_lst])
24 
25 # demo3, 用map,异步计算了,然后结果是返回值的列表
26     re = pool.map(func, range(10))
27     for i in re:print(i)
View Code

(5)进程池回调函数

 1 import os
 2 from multiprocessing import Pool
 3 
 4 def func(num):
 5     print('in func:pid=', os.getpid())
 6     return num*2
 7 
 8 def func2(num):
 9     print('in func2:pid=', os.getpid())
10     num*num
11 
12 if __name__ == '__main__':
13     print('main pid=', os.getpid())
14     pool = Pool(7)
15     res_lst = []
16     for i in range(5):
17         res = pool.apply_async(func, args=(i, ), callback=func2)  # 所谓回调函数,是用func的结果,
18         # 作为func2的参数,# func2是在主进程中运行的!
19         res_lst.append(res)
20     for res in res_lst:print(res.get())
View Code
posted @ 2019-12-30 20:00  四方游览  阅读(168)  评论(0)    收藏  举报