第五十三篇 并发编程之多进程续

第五十三篇 并发编程之多进程续

一、守护进程

1.守护进程的特性

1.守护进程会在主进程代码执行结束后就终止(而守护进程结束不会影响主进程的执行)

2.守护进程内无法再启动子进程,否则会抛出异常:AssertionError: daemonic processes are not allowed to have children

from multiprocessing import Process
import time
import random

class MyProcess(Process):
    def __init__(self, name):
        self.name = name
        super().__init__()
        
    def run(self):
        print(self.name,'start')
        time.sleep(random.randrange(1,4))
        print(self.name,'end')
        
if __name__ == '__main__':
    p = MyProcess('king')
    p.daemon = True
    print('主进程  start')
    p.start()
    time.sleep(3)
    print('主进程  end')

二、进程安全问题

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

2.但是由于共享会带来资源的竞争,竞争的结果就是错乱,所以就出现了进程安全的问题,也就是合理有序的共享有限资源的问题

3.解决方法:

  • 1.直接使用join函数:所有代码全都串行(还不如不开进程),多个进程之间原本公平进制,join则会强行规定执行顺序

  • 2.互斥锁:原理就是将要操作公共资源的代码锁起来,以保证同一时间只能有一个进程在执行这部分代码

三、互斥锁

1.同步的概念

同步就是协同步调,按预定的先后次序进行运行。如:你说完,我再说。"同"字从字面上容易理解为一起动作,其实不是,"同"字应是指协同、协助、互相配合。如进程、线程同步,可理解为进程或线程A和B一块配合,A执行到一定程度时要依靠B的某个结果,于是停下来,示意B运行;B执行,再将结果给A;A再继续操作

2.锁的原理

1.将要操作公共资源的代码锁起来, 以保证同一时间只能有一个进程在执行这部分代码

2.互相排斥的锁

3.优点:可以仅将部分代码串行

4.注意:必须保证锁只有一把

3.锁的应用

1.加锁解决了安全问题,带来了效率减低的问题
2.锁其实只是给执行代码加了限制,本质是一个标志(True或False)
3.如何使得即保证安全又提高效率:

  • 锁的粒度(粒度指的是被锁住的代码的多少),粒度越大锁住的就越多,效率也就越低
from multiprocessing import Process, Lock
import random
import time


def task1(mutex):
    for i in range(10000):
        print(1)

    mutex.acquire()
    time.sleep(random.random())  # 假如随机睡眠,是为了表现两个进程可能同时运行的情况
    print('...task1 start...')
    time.sleep(random.random())
    print('...task1 continue...')
    time.sleep(random.random())
    print('...task1 end...')
    mutex.release()

def task2(mutex):
    for i in range(10000):
        print(2)

    mutex.acquire()
    time.sleep(random.random())
    print('*** task2 start...')
    time.sleep(random.random())
    print('*** task2 continue...')
    time.sleep(random.random())
    print('*** task2 end...')
    mutex.release()

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

    p1.start()
    p2.start()

5.抢票

def show():
    with open('db.json', 'r') as fr:
        data = json.laod(fr)
        print(data['count'])
        
def buy():
    with open('db.json', 'r') as fr:
        data = json.load(fr)
        if data['count'] > 0:
            data['count'] -= 1
            with open('db.json', 'w') as fw:
                json.dump(data, fw)
                print('ok')
                
def task(mutex):
    show()
    mutex.acquire()
    buy()
    mutex.release()
    
if __name__ == '__main__':
    mutex = Lock()
    for i in range(2):
        p = Process(target=task, args=(mutex,))
        p.start()

6.总结

  • 1. 通过multiprocessing导入Lock类
  • 2.进程调用锁的acquire()方法,锁就会进入“locked”状态
  • 3.调用release()方法,锁进入“unlocked”状态

IPC

manager和队列都是基于共享内存而实现的

四、Manager的使用

作用:创建进程间同步的容器
缺点:没有处理安全问题,并不常用

from multiprocessing import Process,Manager,Lock
import time

def task(data,lock):
    lock.acquire()
    num = data[0]
    time.sleep(0.2)
    data[0] = num - 1   # data改变,syncdict的值也会改变
    lock.release()

    print("子 over")


if __name__ == '__main__':

    d = [100]
    m = Manager() # 创建一个管理器
    syncdict = m.dict(d) # 让管理器创建一个进程同步的字典
    # syncdict = m.list(d)    # sync 同步

    # 创建一个锁
    lock = Lock()

    ps = []
    for i in range(10):
        # 
        p = Process(target=task,args=(syncdict,lock))
        p.start()
        ps.append(p)


    for p in ps: p.join()

    print(d)   # 不变
    print(syncdict)  # 改变,和子进程中的同步

五、进程队列

1.Queue 翻译为队列 是一种特殊的容器
2.特殊之处在于存取顺序为先进先出
3.作用:可以帮我们完成进程间通讯

from multiprocessing import Queue

q = Queue(2) # 创建队列 并且同时只能存储2个元素
q.put(1)
q.put(2)

# q.put(3,block=True,timeout=3) # 默认是阻塞的 当容器中没有位置了就阻塞 直到有人从里面取走元素为止
print(q.get())
print(q.get())
print(q.get(block=True,timeout=3))# 默认是阻塞的 当容器中没有位置了就阻塞 直到有人存入元素为止
# block有方块的意思,也有阻塞的意思

六、函数调用栈

1.也是一种特殊的容器
2.存取顺序为先进后出
3.调用函数时,称之为函数入栈(也叫压栈)
4.函数执行结束时,称之为函数出栈

七、处理万能异常

import traceback

try:
    raise Exception('1/0')
except Exception as e:
    traceback.print_exc()   # 可以追踪到具体的出错代码
    print(e)
print('可以执行...')   # 捕捉了异常之后,后面的代码也可以正常执行

八、生产者消费模型

模型就是套路、模板(解决某种固定问题的固定套路)

猜你喜欢

转载自www.cnblogs.com/itboy-newking/p/11185343.html
今日推荐