20181229(守护进程,互斥锁,IPC,生产者和消费者模型)

 

 

一、守护进程

守护进程:一个进程B守护另一个进程A,当被守护的进程A结束,进程B也就结束了。(不一定同生,但会同死)

 

两个特点:

①守护进程会在主进程代码执行结束后就终止

②守护进程内无法再开启子进程,否则抛出异常。

注意:进程之间是互相独立的,主进程代码运行结束,守护进程随即终止

 

应用场景:如果主进程认为一旦自己结束,子进程也就没有继续运行的必要了,就可以将子进程设置为守护进程。(例如:qq正在调用自身的下载文件功能,但是此时退出了qq,下载进程也就可以直接关闭了)

方法为:process.daemon=True,必须放置在子进程开启前。

from multiprocessing import Process
import time

def task(name):
   print('%s is running' % name)
   time.sleep(3)
   print('%s is running' % name)  # 等待三秒的时候,被守护进程已经执行完了,所以守护进程会被回收,此句话就不会打印了。


if __name__ == '__main__':
   obj = Process(target=task, args=('守护进程',))
   obj.daemon=True  # 必须在start前设置,会限制p创建子进程,且父进程结束子进程马上殉葬结束。
   obj.start()  # 发送信号给操作系统
   time.sleep(1)
   print('被守护进程')
输出结果:
守护进程 is running
被守护进程

 

二、互斥锁

互斥锁:将并发变为串行(即一个一个运行)

from multiprocessing import Process,Lock
import time,random

mutex=Lock()  # 实例化互斥锁,也可以放到main下面。
# 强调:必须是lock.acquire()一次,然后 lock.release()释放一次,才能继续lock.acquire(),不能连续的lock.acquire(),否则会形成阻塞,程序卡死。

def task1(lock):
   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()   # 三个子程序都能启动,但是一旦碰到锁,谁先抢到谁先运行,其他的要等。
   p2.start()
   p3.start()

 

互斥锁与join的区别:

二者原理都一样,都是将并发变为串行,从而保证数据增删改查的有序性,不至于让数据发生错乱。

二者的区别在于join是按照人为指定顺序执行,进程执行顺序是固定的,而互斥锁是所有进程公平竞争,谁先抢到锁,谁就能先执行;且互斥锁可以指定某一部分代码串行,其余部分代码并发,而join则是整个子进程代码完全串行。

互斥锁的本质是一个布尔类型的数据,在执行代码前,会先判断这个值。

 

互斥锁在抢票中的应用:

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

# 查看剩余票数
def check_ticket(usr):
   time.sleep(random.randint(1,3))
   with open("ticket.json","r",encoding="utf-8") as f:   # 提前做好json文件,做成字典。
       dic = json.load(f)
       print("%s查看 剩余票数:%s" % (usr,dic["count"]))

def buy_ticket(usr):
   with open("ticket.json","r",encoding="utf-8") as f:
       dic = json.load(f)
       if dic["count"] > 0:
           time.sleep(random.randint(1,3))
           dic["count"] -= 1
           with open("ticket.json", "w", encoding="utf-8") as f2:  #
               json.dump(dic,f2)
               print("%s 购票成功!" % usr)
        else:
           print("票被抢走了!%s购票失败" % usr)


def task(usr,lock):
   check_ticket(usr)  # 查票可以并发
   lock.acquire()  # 排票就要串行了
   buy_ticket(usr)
   lock.release() # 买好票后要释放锁

if __name__ == '__main__':
   lock = Lock()
   for i in range(10):   # 启动10个进程去买票,模仿10个人。
       p = Process(target=task,args=("用户%s" % i,lock))
       p.start()
       
输出结果:   #每次结果可能都不一致
用户0查看 剩余票数:1
用户2查看 剩余票数:1
用户3查看 剩余票数:1
用户1查看 剩余票数:1
用户0 购票成功!
票被抢走了!用户2购票失败
票被抢走了!用户3购票失败
票被抢走了!用户1购票失败
用户5查看 剩余票数:0
票被抢走了!用户5购票失败
用户4查看 剩余票数:0
票被抢走了!用户4购票失败
用户7查看 剩余票数:0
票被抢走了!用户7购票失败
用户6查看 剩余票数:0
票被抢走了!用户6购票失败
用户9查看 剩余票数:0
票被抢走了!用户9购票失败
用户8查看