python-进程间通信-共享内存引发的血案

概述

多进程并发修改同公共变量时,可能会出现bug,看下面的案例:

案例1

淘宝双十一秒杀时,某商品的数量假设是100件,但有101人在秒杀,100件商品就是全局变量,101人就是101个进程,在秒杀时要做判断,如果商品数量大于0就可以秒杀,否则提示售罄,编码如下:

#coding=utf-8

from multiprocessing import Process,Value
import time
import os

#双十一秒杀

def buy(n,index):
    print('%d号买家-开始购买,pid=%d,num=%d'%(index,os.getpid(),n.value))
    #修改变量
    if n.value>0:
        #为了便于查看结果,延迟10秒,开始购买和结束购买会分开
        time.sleep(10)
        n.value-=1
    else:
        print('售罄')
    print('%d号买家-结束购买,pid=%d,num=%d'%(index,os.getpid(),n.value))

if __name__ == "__main__":
    stime=time.time()
    #秒杀商品数量
    num=Value('i',100)
    ps=[]
    for index in range(101):
        p=Process(target=buy,args=(num,index))
        ps.append(p)

    for index in range(101):
        ps[index].start()

    for index in range(101):
        ps[index].join()

    #主进程打印值
    print('主进程,num=%s'%num.value)
    etime=time.time()
    print(etime-stime)

执行结果的部分截图如下:
下图中,在启动进程时,读取的公共变量初始值都是 100,看到此结果,内心就应该颤抖

注意:多进程是由操作系统调度完成,所以每次运行结果不一样

下图中,34号买家、35号买家购买后的数量都是68,出bug了

下图中,最后的结果也不正确

问题原因

执行顺序如下:

  1. 第一步,进程1获取num的值100
  2. 第二步,进程2获取num的值100
  3. 第三步,进程1计算后将99写入num
  4. 第四步,进程2计算后将99写入num

多进程并发访问公共变量时,可能每个进程得到的变量值都是100,计算后赋值时,就都是99

案例2-卖车票

假设有车票500张,有3个售票窗口同时卖票,多进程编程模拟该例子,

注意: 本例sell()函数中有 循环

代码如下:

#coding=utf-8
from multiprocessing import Process,Value
import time
import os

#票总数
count=500
#窗口数量
windownum=3
def sell(n,index):
    print('%d号售票口-开始售票,票号:%d,pid=%d'%(index,n.value,os.getpid()))
    #修改变量
    while n.value<=count:
        print('%d号售票口-售出票号:%d,pid=%d'%(index,n.value,os.getpid()))
        i=n.value+1
        # time.sleep(0.00001)
        n.value=i
        print('%d号售票口-下一票号:%d,pid=%d'%(index,n.value,os.getpid()))
    else:
        print('%d号售票口-售罄'%(index))

if __name__ == "__main__":
    stime=time.time()
    #初始化票号码
    num=Value('i',1)
    ps=[]
    for index in range(windownum):
        p=Process(target=sell,args=(num,index))
        ps.append(p)

    for index in range(windownum):
        ps[index].start()

    for index in range(windownum):
        ps[index].join()

    #主进程打印值
    print('主进程,num=%s'%num.value)
    etime=time.time()
    print(etime-stime)
执行结果部分截图如下:

不同窗口卖出同一张票

注意:多进程是由操作系统调度完成,所以每次运行结果不一样

问题原因

执行顺序如下:

  1. 第一步,进程0获取num值为480
  2. 第二步,进程1获取num值为480
  3. 第三步,进程1计算后将481赋给num
  4. 第四步,进程2获取num值为481
  5. 第五步,进程2计算后将482赋给num
  6. 第六步,进程0计算后将481赋给num

多进程访问公共变量时,可能多个进程得到同一个值,但在赋值时,一定会有前后顺序

结论

多进程修改公共变量时,一个进程的语句只执行一部分,还没执行完,另一个进程参与进来执行,导致公共变量错误


原文出处:/show_1EF2qIGcwKFT.html