python并发编程之守护进程、互斥锁以及生产者和消费者模型

一、守护进程

主进程创建守护进程
守护进程其实就是'子进程'
一、守护进程内无法在开启子进程,否则会报错
二、进程之间代码是相互独立的,主进程代码运行完毕,守护进程也会随机结束

守护进程简单实例:
from multiprocessing import Process
import time
def task(name):                       #此时的task为守护进程
    print('%s is running' % name)     #该行并不会被打印,因为主进程结束,守护进程会随之结束
    time.sleep(3)

if __name__ == '__main__':
    obj = Process(target=task, args=('egon',))
    obj.daemon=True           #一定要在obj.start()开启之前,将obj设置为守护进程,禁止obj创建子进程,否则就会报错
    obj.start()               # 发送信号给操作系统开启一个进程(守护进程)
    print('')               #并且主进程结束,守护进程也会随之结束

 

为什么要用守护进程:

两方面理解:

一、进程:为了让父进程的任务能够并发的执行,需要将该任务放到子进程中去

二、守护:是因为子进程中执行的任务,在父进程运行完就没有存在的意义了,就设置成守护进程,会在父进程结束后,守护进程也随之结束

 

由于计算机的性能的原因,进程打印出的结果可能会有不同的结果:

from multiprocessing import Process
import time
def foo():                      #主进程结束,守护进程就会结束
    print(123)
    time.sleep(1)
    print("end123")

def bar():                      #正常的子进程,父进程会等正常的子进程结束才会结束(父进程可以看到子进程的pid)
    print(456)
    time.sleep(3)
    print("end456")

if __name__ == '__main__':

    p1=Process(target=foo)                #把p1做成守护进程
    p2=Process(target=bar)                #正常的子进程

    p1.daemon=True                #一定要在开启子进程之前,否则会报错,守护进程会伴随主进程的代码的运行完毕而死掉
    p1.start()
    p2.start()
    print("main-------")         #主进程结束,守护进程也随之结束

#正常机器
'''
main-------
456
end456
'''

#快一点机器
'''
main-------     #申请一个内存空间,然后调用操作系统,将其打印到终端
123             #机器性能好的话,可能在打印的时候,p1进程已经起来了,即向操作系统发送请求后,很快就做出回应开启了子进程
456
end456
'''

# 机器更快
'''
123          #当机器的性能在好一点,有可能会出现,p1向操作系统发起开启子进程的请求,迅速做出回应,然后开启子进程,那么123就会优先main先被打印出来
main-------
456
end456
'''
了解知识点

 

二、互斥锁

进程之间数据不共享,但是共享同一套文件系统,所以访问同一套文件,或同一个打印终端是没问题的

然而共享带来的是竞争,竞争带来的就是错乱无序,所以我们要加锁进行处理

多个进程共享同一个打印终端

'''能不自己处理锁,就尽量不用锁,'''
#进程间通信,一个进程修改后,其他进程能看到修改后的结果
'''互斥所就是把并发变成串行,保证了数据安全,但是牺牲了效率'''
'''锁同一时间锁只能被一个子进程抢,用完了再释放掉,才能被其他进程抢到再用'''
"""互斥锁可以将要执行代码的(部分共享的数据)变成串行,而join是将要执行代码所有部分变成串行"""

# 互斥锁:
#强调:必须是lock.acquire()一次,然后 lock.release()释放一次,才能继续lock.acquire(),不能连续的lock.acquire()

# 互斥锁vs join的区别一:
# 大前提:二者的原理都是一样,都是将并发变成串行,从而保证有序
# 区别:join是按照人为指定的顺序执行,而互斥锁是所以进程平等地竞争,谁先抢到谁执行

# 没加锁之前,打印会变的错乱
# 并发运行,效率高,但竞争同一打印终端,带来了打印错乱
from multiprocessing import Process
import time,random

def task1():               #为每一个进程加上一把锁,这样就会有和join一样的效果,变成了串行
    print('task1:名字是egon')
    time.sleep(random.randint(1,3))
    print('task1:性别是male')
    time.sleep(random.randint(1,3))
    print('task1:年龄是18')

def task2():
    print('task2:名字是alex')
    time.sleep(random.randint(1,3))
    print('task2:性别是male')
    time.sleep(random.randint(1,3))
    print('task2:年龄是78')

def task3():
    print('task3:名字是lxx')
    time.sleep(random.randint(1,3))
    print('task3:性别是female')
    time.sleep(random.randint(1,3))
    print('task3:年龄是30')

if __name__ == '__main__':
    p1=Process(target=task1)
    p2=Process(target=task2)
    p3=Process(target=task3)

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

'''
运行结果:
task1:名字是egon
task3:名字是lxx
task2:名字是alex
task1:性别是male
task3:性别是female
task2:性别是male
task1:年龄是18
task3:年龄是30
task2:年龄是78
'''
并发运行,提升了效率,但是共享同一个打印终端,造成打印错乱
from multiprocessing import Process,Lock
import time,random
mutex=Lock()
# 为每一个进程加锁,就可以让其变为串行,牺牲了效率,但保证了数据安全
def task1(lock):               #为每一个进程加上一把锁,这样就会有和join一样的效果,变成了串行
    lock.acquire() #
    print('task1:名字是egon')
    time.sleep(random.randint(1,3))
    print('task1:性别是male')
    time.sleep(random.randint(1,3))
    print('task1:年龄是18')
    lock.release()

def task2(lock):
    lock.acquire()
    print('task2:名字是alex')
    time.sleep(random.randint(1,3))
    print('task2:性别是male')
    time.sleep(random.randint(1,3))
    print('task2:年龄是78')
    lock.release()


def task3(lock):
    lock.acquire()
    print('task3:名字是lxx')
    time.sleep(random.randint(1,3))
    print('task3:性别是female')
    time.sleep(random.randint(1,3))
    print('task3:年龄是30')
    lock.release()


if __name__ == '__main__':
    p1=Process(target=task1,args=(mutex,))
    p2=Process(target=task2,args=(mutex,))
    p3=Process(target=task3,args=(mutex,))

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

    p1.start()
    p2.start()
    p3.start()
加锁:牺牲了效率,但是避免了竞争,打印到同一个终端不会错乱

 

多个进程共享同一份文件--------模拟抢票

通过文件:db.json来模拟数据库

文件内容:

{"count":1}

# 共享一份数据
# 文件db.json内容:
# {'count':1}
import json
import time
import random
import os
from multiprocessing import Process
'''大家抢的是同一份数据,先有查票,然后购票,先在客户端减1,在发送到服务端减1'''
# 查票应该并发
def search():
    time.sleep(random.randint(1,3))        #模拟网络延时
    with open('db.json','r',encoding='utf-8') as f:
        dic=json.load(f)
        print('%s 剩余票数:%s' %(os.getpid(),dic['count']))  #用pi进行标识

# 购票应该一个一个来,才不至于购票时所有人都减1
def get():
    with open('db.json','r',encoding='utf-8') as f:   #之前查看的可能不准确,所以再打开一次
        dic=json.load(f)
    time.sleep(random.randint(1, 3))
    if dic['count'] > 0:
        dic['count']-=1       #在客户端的内存里减1,但要把他刷新到服务端的文件中去
        time.sleep(random.randint(1,3))        #往文件中刷也要有一个网络延迟
        with open('db.json','w',encoding='utf-8') as f:
            json.dump(dic,f)
        print('%s 购票成功' %os.getpid())

def task():
    search()             #先查票,
    get()                #在购票

if __name__ == '__main__':
    for i in range(10):                  #网络延迟已经够把10个进程都创建完
        p=Process(target=task)
        p.start()
        # p.join()                       #加上join后变为串行,但是此时的问题是大家要排队来查票,保证了数据安全,但是效率变低了,因为查票和购票都变成了串行


# 打印结果:
'''
12888 剩余票数:1
10352 剩余票数:1
10440 剩余票数:1
228 剩余票数:1
10736 剩余票数:1
7032 剩余票数:1
8580 剩余票数:1
2712 剩余票数:1
4248 剩余票数:1
2332 剩余票数:1
12888 购票成功
10736 购票成功
228 购票成功
10352 购票成功
10440 购票成功
4248 购票成功
8580 购票成功
2332 购票成功
7032 购票成功
2712 购票成功

'''
并发运行效率高,但是共享同一份文件,写入数据库是错乱
"""
抢票的正确思路是:
查看票数应该是并发进行,提升效率
购票时应加锁,让其串行,虽然降低了效率,但是保证了数据安全即修改时不会导致错乱
"""
import json
import time
import random
import os
from multiprocessing import Process,Lock
'''大家抢的是同一份数据,先有查票,然后购票,先在客户端减1,在发送到服务端减1'''
mutex=Lock()
# 互斥锁vs join的区别一:
# 互斥锁可以让一部分代码(只修改共享数据的代码)变成串行,而join只能将代码整体串行
#查票
def search():
    time.sleep(random.randint(1,3))        #模拟网络延时
    with open('db.json','r',encoding='utf-8') as f:
        dic=json.load(f)
        print('%s 剩余票数:%s' %(os.getpid(),dic['count']))  #用pi进行标识
#购票
def get():
    with open('db.json','r',encoding='utf-8') as f:   #之前查看的可能不准确,所以再打开一次
        dic=json.load(f)
    time.sleep(random.randint(1, 3))
    if dic['count'] > 0:
        dic['count']-=1       #在客户端的内存里减1,但要把他刷新到服务端的文件中去
        time.sleep(random.randint(1,3))        #往文件中刷也要有一个网络延迟
        with open('db.json','w',encoding='utf-8') as f:
            json.dump(dic,f)
        print('%s 购票成功' %os.getpid())

def task(lock):
    search()             #先查票,(是一个单独的行为,等你查完票在取购票的时候,可能票已经被抢完了)

    # 购票变成串行,对get即购票进行加锁处理
    lock.acquire()       #mutex=Lock().acquire()---------互斥所不能连续的acquire,必须释放掉才能在acquire,(有种独享的感觉)
    get()                #在购票
    lock.release()       #mutex=Lock().release()--------(释放掉锁,这把锁可以在被其他人抢到)

if __name__ == '__main__':
    for i in range(10):                  #网络延迟已经够把10个进程都创建完
        p=Process(target=task,args=(mutex,))
        p.start()
        # p.join()
加锁:购票行为由并发变为串行,降低了效率,但是保证了数据安全

 

 

三、IPC通信机制

四、生产者和消费者模型

 

猜你喜欢

转载自www.cnblogs.com/sui776265233/p/9300203.html