python多进程--python之旅的首个坑

注意这里是多进程而不是多线程

一,先看多进程数据交互,使用Queue或Pipes这些multiprocessing模块封装好的交换方式。

from multiprocessing import Process , Queue
import os, time, random

def write(q):
    print('write child process(%s) start..' % os.getpid())
    for i in ['你好', '大头', '歌曲']:
        print('write child process(%s) write %s' % (os.getpid(),i))
        q.put(i)        #把数据写入Queue
        time.sleep(random.random())
    print('write child process(%s) finish..' % os.getpid())

def read(q):
    print('read child process(%s) start..' % os.getpid())
    while True:
        data = q.get(True)  #读取数据
        print('read child process(%s) read %s' % (os.getpid(),data))
        time.sleep(random.random())
    print('read child process(%s) finish..' % os.getpid())

if __name__ == '__main__':
    print('parent process(%s) start...' % os.getpid())
    q = Queue()  #只能用Queue,Pipes等来交换数据,如果试图用list这种效果就和值传递一样,起不了作用
    pw = Process(target=write, args=(q,))
    pr = Process(target=read, args=(q,))
    pw.start()
    pr.start()
    pw.join()
    time.sleep(7)      #父进程睡7秒保证,保证读进程能读完数据
    pr.terminate()    #读写成死循环,强行终止掉
    print('parent process(%s) end...' % os.getpid())

输出:

parent process(2412) start...
write child process(3584) start..
write child process(3584) write 你好
read child process(13740) start..
read child process(13740) read 你好
write child process(3584) write 大头
write child process(3584) write 歌曲
write child process(3584) finish..
read child process(13740) read 大头
read child process(13740) read 歌曲
parent process(2412) end...

注释已经写的比较详细了,可以看得出来,只需要用mulprocessing模块封装好的交换方式作为参数传递给子进程,那么这个参数就在这些进程中共享了,只需要查阅文档学习这些交换方式如何使用即可。

简单记录一下:

  • multiprocessing.Queue,是多进程安全的队列(FIFO)
  • multiprocessing.Manager,封装了可用于多进程的常用数据类型,例如list和dict
  • multiprocessing.Pipe,  是管道通信

二,Pool, 提到多任务肯定就离不开池的概念

池的想法很简单,因为创建一个进程开销大,所以可以先创建一堆进程,然后不关闭它们,重复利用这些进程,这就是进程池。同样的道理,可以扩展到线程池,数据库连接池,Http长连接等。

from multiprocessing import Pool
import os, random, time

def task(name):
    print('child process(%s) do task(%s) start...' % (os.gepid(),name))
    time.sleep(random.random())
    print('child process(%s) do task(%s) end...' % (os.gepid(), name))

if __name__ == '__main__':
    print('parent process(%s) start' % os.getpid())

    p = Pool(3) #进程池里一共3个进程 如果不写参数就是你的电脑线程数 比如6核12线程的cpu的话,那就默认12个
    for i in range(1,6):
        p.apply_async(task, args=(i,))  #向进程池中投放任务

    p.close()
    p.join()

    print('parent process(%s) end' % os.getpid())

Output:
parent process(6472) start
parent process(6472) end

一切都很简单,然后可以清楚的看到子进程的print没有打印。这里终于遇到了python的首个坑,

经过一番查阅,得到的原因是:

这些子进程其实是成功执行了的,只不过由于,multiprocess封装了fork,而windows又不支持fork(于是在windows上就自己模拟了fork,需要把对象都pickle序列化),这里出了问题。或者可有可能是IDE的输出不支持。

那么解决方法有:

  • 换一个mac电脑
  • 装一个Linux系统
  • 输出子进程的日志到文件
  • 在夫进程中获取子进程的返回值

三,一些问题和参考

1.可以注意到前面都有__name__ == '__main__'的判断,如果不加为什么会报错?

参考:https://blog.csdn.net/llf_cloud/article/details/81775410

这是 Windows 上多进程的实现问题。在 Windows 上,子进程会自动 import 启动它的这个文件,而在 import 的时候是会执行这些语句的。如果你这么写的话就会无限递归创建子进程报错。但是在multiprocessing.Process的源码中是对子进程再次产生子进程是做了限制的,是不允许的,于是出现如上的错误提示。所以必须把创建子进程的部分用那个 if 判断保护起来,import 的时候 name 不是 main ,就不会递归运行了。
2.一篇比较详细的文章

https://thief.one/2016/11/23/Python-multiprocessing/

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值