113 Python程序中的进程操作-进程同步(multiprocess.Lock)

尽管并发编程让我们能更加充分的利用IO资源,但是也给我们带来了新的问题:当多个进程使用同一份数据资源的时候,就会引发数据安全或顺序混乱问题。

一、进程同步

多个进程同时执行,为了相互制约各进程对资源的访问,使得各个进程的执行相互同步。

在我的理解里,进程同步也算是进程间通讯(ipc)的一种手段。

二、为什么需要进程同步

多进程会引发抢占资源的问题,为了解决这个问题就需要各进程之间相互同步,控制进程对关键资源的访问。

在C语言中实现进程同步的方式有很多,比如:信号量,锁机制等。

例子: 演示多进程资源抢占问题

import os
import time
import random
from multiprocessing import Process

def work(n):
    print('%s: %s is running' %(n,os.getpid()))
    time.sleep(random.random())
    print('%s:%s is done' %(n,os.getpid()))

if __name__ == '__main__':
    for i in range(3):
        p=Process(target=work,args=(i,))
        p.start()

三、Python中实现进程同步

  1. 首先from multiprocessing import Lock
  2. 在主进程中,实例化得到锁,lock = Lock(),并传给子进程
  3. 在子进程中通过上锁和解锁实现对多进程对资源的控制。lock.acquire()上锁,lock.release()解锁

演示:通过Python锁机制控制资源的访问

def work(lock,n):
    print(f"等待开锁 进程{n}")
    # 上锁
    lock.acquire()     # 当上锁之后别的进程无法访问,会阻塞住
    print(f'进程{n} pid: %s is running' % ( os.getpid()))
    time.sleep(random.random())
    print(f'进程{n} pid: %s is done' % (os.getpid()))
    # 开锁
    lock.release()

if __name__ == '__main__':
    lock=Lock() # 实例化得到锁
    for i in range(3):
        p=Process(target=work,args=(lock,i))
        p.start()

等待开锁 进程1
进程1 pid: 8696 is running
等待开锁 进程0
等待开锁 进程2
进程1 pid: 8696 is done
进程0 pid: 14264 is running
进程0 pid: 14264 is done
进程2 pid: 22724 is running
进程2 pid: 22724 is done

根据结果可以发现,在上锁之前的代码。多进程是并发访问的,但上锁之后,直到解锁后才能有第二个人访问。以此类推。就好像上厕所排队一样,进去一个人关上门锁上,第二个人等着。

但是看完你会有个疑问,那被锁上的代码,每个进程在访问的时候不就是串行的依次在访问嘛。

确实,锁机制 保证了数据的安全,但牺牲掉效率.

四、多进程模拟同时抢票

# 文件db的内容为:{"count":1}
# 注意一定要用双引号,不然json无法识别
# 并发运行,效率高,但竞争写同一文件,数据写入错乱
from multiprocessing import Process,Lock
import time,json,random
def search():
    dic=json.load(open('db'))
    print('剩余票数%s' %dic['count'])

def get():
    dic=json.load(open('db'))
    time.sleep(0.1)  # 模拟读数据的网络延迟
    if dic['count'] >0:
        dic['count']-=1
        time.sleep(0.2)  # 模拟写数据的网络延迟
        json.dump(dic,open('db','w'))
        print('购票成功')

def task():
    search()
    get()

if __name__ == '__main__':
    for i in range(100):  # 模拟并发100个客户端抢票
        p=Process(target=task)
        p.start()

上述代码,你会发现多个进程在同时抢票,相互抢占文件资源,每个进程都把文件读入到内存中进行抢票。实际上,资源只能被一个人占有,这就会引发问题

4.1 通过锁控制进程资源访问

def search():
    time.sleep(1)
    with open("db","w",encoding="utf8") as f:
        data = json.load(f)
        print(f'还剩{data["count"]}')

def get():
    with open('db','rt',encoding='utf-8') as f:
        res = json.load(f)
    time.sleep(1)  # 模拟网络io
    if res['count'] > 0:
        res['count'] -= 1
        with open('db', 'w', encoding='utf-8') as f:
            json.dump(res, f)
            print(f'进程{os.getpid()} 抢票成功')
        time.sleep(1.5)  # 模拟网络io
    else:
        print('票已经售空啦!!!!!!!!!!!')

def task(lock):
    print(f"进程:{os.getpid()}  正在抢票中。。。")

    # 上锁     每个进程都会访问一遍,所以加锁就等于上锁到解锁这段代码是串行的
    lock.acquire()  # 当上锁之后别的进程无法访问,会阻塞住
    get()
    # 开锁
    lock.release()

if __name__ == '__main__':
    # 创建锁
    lock = Lock()

    pro_list = []
    # 创建进程
    for i in range(10):
        p = Process(target=task, args=(lock,))
        p.start()
        pro_list.append(p)
    # 回收进程
    for i in range(10):
        pro_list[i].join()

总结

进程同步理论上应该是进程间通讯中的一部分,是一个大话题,每个语言也都有实现进程同步的需求。需要将各个进程对资源的访问加以限制。在各个语言中也都有限制。这里目前只介绍了这一种进程同步的方法。

锁机制 是把锁住的代码变成了串行,保证了数据的安全,但牺牲掉效率.

猜你喜欢

转载自www.cnblogs.com/XuChengNotes/p/11529599.html
113