python基础-进程互斥锁、线程互斥锁

进程错乱

多进程共享同一套文件系统,访问同一个文件,或同一个打印终端,是没有问题的
但是进程之间数据不共享,多个进程共同操作数据会出现错乱,我们来看一个例子

from multiprocessing import Process
import os,time
def work():
    print('%s is running' %os.getpid())
    time.sleep(2)
    print('%s is done' %os.getpid())

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

输出如下:

14400 is running
368 is running
13808 is running

14400 is done
368 is done
13808 is done

看到的现象是多个子进程循环执行,由于循环瞬间执行完毕,就可以当作多个子进程同时进行,然后 time.sleep(2),又可以看多个子进程同时剩下的任务代码

进程join引入互斥锁

在介绍互斥锁,我们可以利用join来伪实现下,但是join实现的跟互斥锁是不一样的效果

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

def task():
    print('%s print 1' %os.getpid())
    time.sleep(random.randint(1,3))
    print('%s print 2' %os.getpid())

if __name__ == '__main__':
    p1=Process(target=task)
    p2=Process(target=task)
    p3=Process(target=task)

    p1.start()
    p1.join()
    p2.start()
    p2.join()
    p3.start()
    p3.join()

输出如下:

E:\python\python_sdk\python.exe "E:/python/py_pro/上课内容/2 互斥锁-进程.py"
11076 print 1

11076 print 2
1276 print 1

1276 print 2
10316 print 1

10316 print 2

Process finished with exit code 0

通过join操作,从效果上也实现了子进程并行的效果,但是这样的并行效果,是让其中一个子进程运行,在这个子进程运行完毕,在让另外一个子进程运行,所以在其中一个子进程运行期间,其他进程只能按着固有的顺序,我们看下利用互斥锁是如何实现的

进程互斥锁

竞争带来的结果就是错乱,如何控制,就是加锁处理

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

def task(mutex):
    mutex.acquire()
    print('%s print 1' %os.getpid())
    time.sleep(random.randint(1,3))
    print('%s print 2' %os.getpid())
    time.sleep(random.randint(1, 3))
    print('%s print 3' %os.getpid())
    mutex.release()
    print("---------")


if __name__ == '__main__':
    mutex=Lock()
    p1=Process(target=task,args=(mutex,))
    p2=Process(target=task,args=(mutex,))
    p3=Process(target=task,args=(mutex,))

    p1.start()
    print(p1.pid)

    p2.start()

    p3.start()
    print(p3.pid)

输出如下:

E:\python\python_sdk\python.exe "E:/python/py_pro/上课内容/2 互斥锁-进程.py"
2348
1040
10064
2348 print 1
2348 print 2
2348 print 3
---------
1040 print 1
1040 print 2
1040 print 3
---------
10064 print 1
10064 print 2
10064 print 3
---------

Process finished with exit code 0

总结:
加锁可以保证多个进程修改同一块数据时,同一时间只能有一个任务可以进行修改,即串行的修改,没错,速度是慢了,但牺牲了速度却保证了数据安全。
虽然可以用文件共享数据实现进程间通信,但问题是:
1.效率低(共享数据基于文件,而文件是硬盘上的数据)
2.需要自己加锁处理

购票例子(进程互斥锁)

db.text

{"count": 1}

模拟购票代码

from  multiprocessing import Process,Lock
import json
import os
import time
import random

def search():
    with open('db.txt',encoding='utf-8') as f:
        dic=json.load(f)
        print('%s 剩余票数 %s' %(os.getpid(),dic['count']))

def get():
    with open('db.txt',encoding='utf-8') as read_f:
        dic=json.load(read_f)

    if dic['count'] > 0:
        dic['count']-=1
        time.sleep(random.randint(1,3)) #模拟手速+网速
        with open('db.txt','w',encoding='utf-8') as write_f:
            json.dump(dic,write_f)
            print('%s 抢票成功' %os.getpid())

def task(mutex):
    search()
    mutex.acquire()
    get()
    mutex.release()

if __name__ == '__main__':
    mutex=Lock()
    for i in range(5):
        p=Process(target=task,args=(mutex,))
        p.start()

输出如下:

E:\python\python_sdk\python.exe "E:/python/py_pro/上课内容/3 模拟抢票.py"
2084 剩余票数 1
11400 剩余票数 1
11736 剩余票数 1
8628 剩余票数 1
14608 剩余票数 1
2084 抢票成功

Process finished with exit code 0

线程互斥锁

from threading import Thread,Lock
import time
n=100

def task():
    global n
    mutex.acquire()
    temp=n
    time.sleep(0.1)
    n=temp-1
    mutex.release()

if __name__ == '__main__':
    mutex=Lock()
    t_l=[]
    for i in range(10):
        t=Thread(target=task)
        t_l.append(t)
        t.start()

    for t in t_l:
        t.join()

    print(n)

输出如下:

90

意思就是启动10个子线程,然后为10个子线程分别加线程互斥锁,操作变量n,然后在各个子线程执行完毕,在输出n的值为90,说明改变了共享变量n

线程锁注意的点如下:
1.线程抢的是GIL锁,GIL锁相当于执行权限,拿到执行权限后才能拿到互斥锁Lock,其他线程也可以抢到GIL,但如果发现Lock仍然没有被释放则阻塞,即便是拿到执行权限GIL也要立刻交出来

2.join是等待所有,即整体串行,而锁只是锁住修改共享数据的部分,即部分串行,要想保证数据安全的根本原理在于让并发变成串行,join与互斥锁都可以实现,毫无疑问,互斥锁的部分串行效率要更高

mutex的简写形式

关于互斥锁可以简写成如下:

def task():
    global n
    mutex.acquire()
    temp=n
    time.sleep(0.1)
    n=temp-1
    mutex.release()

def task():
    global n
    with mutex:
        temp=n
        time.sleep(0.1)
        n=temp-1
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值