python进程间通信:win和linux写法差别!

使用的包:multiprocessing
使用其中的模块:Process、Pipe、Queue、Value、Array

说明:虽然multiprocessing理论上是跨平台均可使用,但是win和linux在语法上还是有些差别!本人原始在ubuntu下运行进程间通信都很顺畅,但同样的文件在win下就通不过!原因其实很简单:win下写法要更加严格

Process模块使用区别

在linux下的进程创建、激活以及父进程处理僵尸子进程的方式:

from multiprocessing import Process
import time

def fun(num):
    print('ceshi:', num)

pp = []
# 子进程的创建与激活
for x in range(10):
    p = Process(target = fun, args = (x,))
    pp.append(p)
    p.start()

# 以下全是父进程语句:
# 父进程处理子进程
for x in pp:
    x.join()

print('父进程处理完毕')

这是最简单的写法,但是在win下运行会报这样的错误:
The "freeze_support()" line can be omitted if the program
is not going to be frozen to produce an executable.

按照错误提示,我们只需要把主函数的语句,放到主函数里即可;因此可得win下的程序:

from multiprocessing import Process
import time

def fun(num):
    print('ceshi:', num)

if __name__ == '__main__':
    pp = []
    # 子进程的创建与激活
    for x in range(10):
        p = Process(target = fun, args = (x,))
        pp.append(p)
        p.start()

    # 以下全是父进程语句:
    # 父进程处理子进程
    for x in pp:
        x.join()

    print('父进程处理完毕')

Pipe模块使用区别

Pipe也是multiprocessing包下的一个模块,

  • 作用:在内存中开辟一块管道空间来帮助多进程间进行信息传递。
  • 使用:和Process模块不太一样,因为"管道"空间需要你在程序中人为声明一下,即多一条声明语句。

linux下程序:

from multiprocessing import Process,Pipe
import os,time

# 管道声明语句:必须放在最前面!!!
double1_conn, double2_conn = Pipe()

def fun1(name):
    time.sleep(1)
    double1_conn.send("ceshi" + str(name)) # 子进程往管道空间发消息
    print('父进程PID号:', os.getppid(), ";", '子进程PID号:', os.getpid())

# 子进程创建激活语句:
jobs = []
for i in range(5):
    p = Process(target = fun1, args = (i,))
    jobs.append(p)
    p.start()

# 下面全是父进程语句:
# 父进程对子进程的处理
for i in jobs:
    i.join()

# 父进程从管道空间取数据:
print('父进程从管道空间中取消息:')
for i in range(5):
    data = double2_conn.recv()
    print(data)

同样,这样的语句在win下运行还是会报和上面同样的错误!
还是提示我们要把主函数的语句下近一个主函数里:

from multiprocessing import Process,Pipe
import os,time

# 注意:传入的参数中要有一个"管道变量"!
# 因为:原本最开始的管道声明语句,现在是在fun1函数的下面!
def fun1(P, name):
    time.sleep(1)
    P.send("ceshi" + str(name))
    print('父进程PID号:', os.getppid(), ";", '子进程PID号:', os.getpid())

if __name__ == '__main__':

    double1_conn, double2_conn = Pipe()  # 管道声明语句必须放进来!

    jobs = []
    for i in range(5):
        p = Process(target = fun1, args = (double1_conn,i))
        jobs.append(p)
        p.start()

    for i in jobs:
        i.join()

    for i in range(5):
        data = double2_conn.recv()
        print(data)

注意两点:

  • 管道声明必须也放进下面的主函数语句里!不能再放到文件的最前面!
  • 函数传参时,必须传进去一个"管道变量(如fun1中第一个形参P)"才能在函数里发送或接受管道的信息!因为原始的管道声明语句已经放到了函数声明语句的下面。

Queue模块使用区别

另外一种进程间通信方法,使用和Pipe很像,也需要在程序中受到声明一下,在内存中开辟队列空间。

linux下程序:

from multiprocessing import Queue, Process
import time

que = Queue(10) # 队列的声明要放最前面

# 子进程的功能函数
def fun(name):
    time.sleep(1)
    que.put('ceshi' + str(name))
    print('存入一条消息', '队列中现有 %s 条消息' % que.qsize())

# 进程的创建:
jobs = []
for x in range(10):
    p = Process(target = fun, args=(x,))  
    jobs.append(p)  
    p.start()  

# 处理子进程
for x in jobs:
    x.join()

# 下面是父进程:
print('当前队列中有:', que.qsize(), '条消息')
while not que.empty():
    print('拿到的消息:', que.get(), ';', '还有 %d ' % que.qsize())

不改写的话,非常有可能会报和上面一样的错误;此外,就算有些win下可以通过,它会一直显示你没有成功得把消息传进队列当中!队列中一直只有1条消息。
因此,win下程序改写:

from multiprocessing import Queue, Process
import time

# 函数传参:必须要把绑定队列的变量传进来!
# 因为:同样声明语句已经放到了函数的下面,必须要传进来
def fun(que, name):
    time.sleep(1)
    que.put('ceshi' + str(name))
    print('存入一条消息', '队列中现有 %s 条消息' % que.qsize())

if __name__ == '__main__':

    que = Queue(10) # 队列声明必须要放进来!

    # 进程的创建:
    jobs = []
    for x in range(10):
        p = Process(target = fun, args=(que, x))
        jobs.append(p)  
        p.start() 

    # 处理子进程
    for x in jobs:
        x.join()

    # 下面是父进程:
    print('当前队列中有:', que.qsize(), '条消息')
    while not que.empty():
        print('拿到的消息:', que.get(), ';', '还有 %d ' % que.qsize())

同样注意两点:

  • 队列的声明也必须放进主函数语句里,且放在其中的最前面;
  • 函数传参时,必须把"绑定队列的那个变量"传进去,才能在函数里发送或提取队列的信息!因为原始的队列声明语句已经放到了函数声明语句的下面。

Value模块使用区别

  • 作用:创建贡献内存,大家共同修改一个参数
  • 使用:和管道差不多

linux下程序:

from multiprocessing import Value, Process
import time
import random

money = Value('i', 5000)  # 开辟共享内存,初始值给2000

# 子进程1:存钱
def deposite(money):
    for i in range(100):
        time.sleep(0.03)
        money.value += random.randint(1,150)

# 子进程2:取钱
def withdraw(money):
    for i in range(100):
        money.value -= random.randint(1,150)

# 创建子进程:
d = Process(target = deposite, args = (money,))
w = Process(target = withdraw, args = (money,))

# 激活子进程:
d.start()
w.start()

# 下面都是父进程的语句:
# 父进程处理子进程:
d.join()
w.join()

# 父进程查看最后共享内存里的那个数值:
print(money.value)

win下程序:

from multiprocessing import Value, Process
import time
import random

# 注意:传入的参数是绑定那个共享内存空间的变量!!!
# 子进程1:存钱
def deposite(money):
    for i in range(100):
        time.sleep(0.03)
        money.value += random.randint(1,150)

# 子进程2:取钱
def withdraw(money):
    for i in range(100):
        money.value -= random.randint(1,150)

if __name__ == '__main__':

    money = Value('i', 5000)  # 共享内存声明语句,必须写到里面

    # 创建子进程:
    d = Process(target = deposite, args = (money,))
    w = Process(target = withdraw, args = (money,))

    # 激活子进程:
    d.start()
    w.start()

    # 父进程处理子进程:
    d.join()
    w.join()

    # 父进程查看最后共享内存里的那个数值:
    print(money.value)

注意两点:

  • 共享内存声明语句,必须写到主函数里,且在其中的最上面;
  • 函数传参:传入的是绑定那个共享内存空间的变量!这个绑定的变量有自己的属性,可以获取或修改对应的共享内存中的数值。

Array模块使用区别

  • 作用:共享内存创建的另一种形式,相比与value模块往共享内存中放入的是一个数值,array往共享内存中存放的是一个"元素类型必须相同"的列表

linux下程序:

from multiprocessing import Process, Array
import time

# 声明创建一个共享内存,其中初始化一个列表
shm = Array('i', [1,2,3])

def fun(shm):
    for i in range(len(shm)):
        shm[i] = 99

# 创建子进程:
p = Process(target=fun, args=(shm,))

# 激活子进程:
p.start()

# 父进程处理子进程:
p.join()

# 父进程获取新的共享内存中数列表:
for i in shm:
    print(i)

win下的函数:

from multiprocessing import Process, Array
import time

# 修改共享内存中数组里的值
def fun(shm):
    for i in range(len(shm)):
        shm[i] = 99

if __name__ == '__main__':

    shm = Array('i', [1, 2, 3])  # 声明创建一个共享内存,要放到里面

    # 创建子进程:
    p = Process(target = fun, args = (shm,))

    # 激活子进程:
    p.start()

    # 父进程处理子进程:
    p.join()

    # 父进程获取新的共享内存中数列表:
    for i in shm:
        print(i)

注意:使用注意事项和value一样。

说明

上面所有的linux程序"不能"在win下正常运行;
但所有的win程序"可以"在linux下正常运行!!!
很明显:win下的程序写法更加正式、标准、通用!所以以后写进程间通信的程序,直接按照win要求的这种写法去写即可,只有好处没有坏处。

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 219,869评论 6 508
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 93,716评论 3 396
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 166,223评论 0 357
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 59,047评论 1 295
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 68,089评论 6 395
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 51,839评论 1 308
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 40,516评论 3 420
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 39,410评论 0 276
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 45,920评论 1 319
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 38,052评论 3 340
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 40,179评论 1 352
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,868评论 5 346
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 41,522评论 3 331
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 32,070评论 0 22
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 33,186评论 1 272
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 48,487评论 3 375
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 45,162评论 2 356

推荐阅读更多精彩内容