并发编程之进程基础

一、join() 方法

阻塞当前进程,直到调用join方法的那个进程执行完,再继续执行当前进程

控制进程是否结束,进程结束就结束阻塞

使进程处于阻塞状态,直到进程执行完就结束阻塞

相当于同步控制

from multiprocessing import Process

def sendmail(argv):
    print('快递%d已送达'%argv)

if __name__ == '__main__':
    li = []
    for i in range(10):
        p = Process(target=sendmail,args=(i,))      # 创建子进程对象
        p.start()                                   # 启动子进程
        li.append(p)                                # 将对象假如到列表
    for p in li:                                    # 循环对象的列表
        p.join()                                    # 使进程处于阻塞状态,直到进程执行完就结束阻塞
    print('10件快件已发送完毕')
    
'''
结果
快递1已送达
快递0已送达
快递2已送达
快递4已送达
快递3已送达
快递5已送达
快递6已送达
快递8已送达
快递7已送达
快递9已送达
10件快件已发送完毕
'''

 

二、第二种开启子进程的方式

面向对象的方式启动,创建类且继承Process方法,并重写run方法

start()是开启子进程,让子进程执行run方法

注意:多个进程不能同时往一个文件写,造成资源抢占,令指针混乱

from multiprocessing import Process
import os
class Myprogress(Process):              # 继承rocess类
    def run(self):                      # 重写run方法
        print('子进程:%d,父进程:%d'%(os.getpid(),os.getppid()))

if __name__ == '__main__':
    for i in range(5):
        p = Myprogress()                # 创建对象
        p.start()                       # 开启进程
    print('主进程:%d'%os.getpid())
    
'''
结果
主进程:484
子进程:8752,父进程:484
子进程:11244,父进程:484
子进程:9804,父进程:484
子进程:12792,父进程:484
子进程:6868,父进程:484
'''

 

传参数给子进程

from multiprocessing import Process

class Mycourse(Process):
    def __init__(self,argv):        # 初始化方法
        super().__init__()          # 继承父类的初始化方法
        self.argv = argv            # 创建一个变量,用于传参

    def run(self,):                 # 启动进程执行的方法
        print('子进程%d号'% self.argv)

if __name__ == '__main__':
    for i in range(1,10,2):
        p = Mycourse(i)
        p.start()
    print('主进程')

'''
结果
主进程
子进程3号
子进程1号
子进程5号
子进程7号
子进程9号
'''

 

第二种方式开启子进程并加入join方法

from multiprocessing import Process

class Mycourse(Process):
    def __init__(self,argv):        # 初始化方法
        super().__init__()          # 继承父类的初始化方法
        self.argv = argv            # 创建一个变量,用于传参

    def run(self,):                 # 启动进程执行的方法
        print('子进程%d号'% self.argv)

if __name__ == '__main__':
    pro_lis = []
    for i in range(1,10,2):
        p = Mycourse(i)
        p.start()
        pro_lis.append(p)
    for p in pro_lis:
        p.join()                    # 阻塞当前进程,直到调用join方法的那个进程执行完,再继续执行当前进程
    print('主进程')

'''
结果
子进程1号
子进程7号
子进程3号
子进程5号
子进程9号
主进程
'''

三、守护进程

p.daemon = True 设置p进程为守护进程,必须在start方法之前完成

守护进程会随着主进程的代码执行完毕而结束

主进程代码已经执行完,但是子进程还没执行完,守护进程都不会继续执行。

子进程要在主进程结束前结束

主进程会等待子进程结束,守护进程之等待主进程代码结束就结束了

守护进程的作用:程序的报活

from multiprocessing import Process
import time
def func():
    for i in range(1,10):
        print('子进程%d号'% i)
        time.sleep(1)

if __name__ == '__main__':
    p = Process(target=func)
    p.daemon = True             # 设置进程为守护进程
    p.start()
    time.sleep(3)
    print('主进程结束')
    
'''
结果
子进程1号
子进程2号
子进程3号
主进程结束
'''

四、其他方法

p.terminate()#关闭进程,不会立即关闭,所以is_alive立刻查看的结果可能还是存活

p.pid  查看子进程的pid

from multiprocessing import Process
import time
import os

def func():
    for i in range(1,10):
        time.sleep(1)
        print('%d秒' % i,'进程号:',os.getpid())

if __name__ == '__main__':
    p = Process(target=func)
    p.start()
    print(p.pid)            # 查看子进程的pid号
    time.sleep(3)
    p.terminate()           # 关闭子进程进程,但不是马上关闭
    print(p.is_alive())     # 查看子进程的状态
    time.sleep(1)
    print(p.is_alive())     # 查看子进程的状态

'''
结果
10720
1秒 进程号: 10720
2秒 进程号: 10720
True
False
'''

五、锁(互斥锁)

步骤:

实例化锁对象,把锁对象传给子进程

锁对象.acquire()  给代码上锁

锁对象.release()  释放锁

上锁为了保证数据的安全,在异步的情况下,多个进程有可能同时修改同一份资源,就给修改的过程加上锁

加锁降低了程序的效率,让原来能够同时执行的代码变成了顺序执行了,异步变同步的过程,保证了数据的安全

from multiprocessing import Process
from multiprocessing import Lock
import json

def get_ticket(name,lock):
    lock.acquire()
    with open('ticket') as f:
        dic = json.load(f)
    if dic['count'] > 0:
        with open('ticket','w') as f1:
            dic['count'] -= 1
            json.dump(dic,f1)
            print('%s已买票'% name)
    else:print('%s走路回家'% name)
    lock.release()


if __name__ == '__main__':
    name_lis = ['小明','小红','小蓝','小黑','小白','小青','小小','小树']
    lock = Lock()           # 创建锁对象
    for i in range(8):
        p = Process(target=get_ticket,args=(name_lis[i],lock))
        p.start()

'''
小明已买票
小红已买票
小蓝已买票
小黑已买票
小白已买票
小青走路回家
小小走路回家
小树走路回家
'''

死锁:在同一个级才能中,连续acquire()两次或以上,程序就不能继续执行,造成了死锁现象

六、信号量

锁的变形,多个锁,使部分进程可以执行代码

Semaphore信号量,保证资源不会被多进程执行

使用方法跟锁类似

信号量的实现机制:用计算器+锁实现

from multiprocessing import Process
from multiprocessing import Semaphore
import time

def get_ticket(name,sem):
    sem.acquire()
    print('%s 打饭'% name)
    time.sleep(2)
    print('%s 离开' % name)
    sem.release()


if __name__ == '__main__':
    name_lis = ['小明','小红','小蓝','小黑','小白','小青','小小','小树']
    sem = Semaphore(4)           # 创建信号量对象
    for i in range(8):
        p = Process(target=get_ticket,args=(name_lis[i],sem))
        p.start()
        
'''
结果
小明 打饭
小红 打饭
小蓝 打饭
小黑 打饭
小明 离开
小青 打饭
小红 离开
小白 打饭
小蓝 离开
小小 打饭
小黑 离开
小树 打饭
小青 离开
小白 离开
小小 离开
小树 离开
'''

七、事件

Event 模块

方法: wait()

wait是否阻塞使看event对象内部的一个属性的值,默认是False

控制属性的值的方法:

set()    将属性的值改为True

clear()    将属性的值改为False

is_set()   判断当前属性的值是否为True

例子:红绿灯

from multiprocessing import Event
from multiprocessing import Process
import time

def traffic_light(e):
    while 1:
        if e.is_set():          #
            e.clear()           # 设置状态为False
            print('33[31m红灯亮33[0m')
            time.sleep(3)


        else:
            print('33[32m绿灯亮33[0m')
            e.set()             # 设置状态为True
            time.sleep(5)


def car(e,name):
    e.wait()
    time.sleep(0.5)
    if e.is_set():
        print('%s 通行了' % name)

if __name__ == '__main__':
    cat_list = ['昌河','奇瑞','东风','吉利','长城','一汽']
    e = Event()
    p1 = Process(target=traffic_light,args=(e,))
    p1.daemon = True             # 将红绿灯设置为守护进程
    p1.start()

    p2_lis = []
    for i in range(6):
        p2 = Process(target=car,args=(e,cat_list[i]))
        p2.start()
        p2_lis.append(p2)
        time.sleep(2)
        p2.join()               # 设置当前进程阻塞,使子进程执行完才执行当前进程
    print('车辆全部通过')

'''
绿灯亮
昌河 通行了
奇瑞 通行了
东风 通行了
红灯亮
绿灯亮
吉利 通行了
长城 通行了
一汽 通行了
车辆全部通过
'''
原文地址:https://www.cnblogs.com/st-st/p/9674787.html