Python——multiprocessing(进程模块)

 注意点:

1. 各操作系统中创建进程的方法不同。运行方式也有所不同。

 

windows创建进程:

执行开启进程的代码

1. Process实例化后,.start()来创建一个进程,并告诉系统,创建完进程后请执行Process内的target所指定的函数。

2. 由于是进程之间数据无法交互的问题,那么windows会以import的方式来将父进程的代码导入到子进程中,并运行。

3. 如果此时没有__name__判断语句,那么将会继续执行Process语句进行子进程的创建。反复创建直到报错。

IOS Linux操作系统创建进程:

 是使用fork来将父进程代码运算后得出的值进行直接的拷贝(将内存地址直接拷贝),而不是在子进程中再运行一遍。

使用multiprocessing模块来创建一个进程。

from multiprocessing import Process
import time
import os


def func():
    print('A',os.getpid())
    time.sleep(1)
    print('B',os.getpid())


if __name__ == '__main__':            #windows中必须要写,如果不写将会报错
    P = Process(target=func)          #准备创建一个进程,并将func函数加入其中
    P.start()                         #将func函数丢给系统,让系统自行创建一个进程,并运行其中的代码。
    print('C',os.getpid())            #自己将继续往下执行自己进程中的事情。
    exit()
    print('D', os.getpid())

'''
C 17344
A 9400
B 9400
在P.start时,程序会将func代码直接给系统,让系统创建一个进程然后执行func中的代码。而不去管执行的怎么样,自己则继续往下执行。
因为CD中间有一个退出语句,那么就代表直接关闭该进程,而导致不会继续该进程下的D输出。
'''

方法和功能:

 

传参给要运行的函数。

Process(target=func,args=(a,))

必须是元组形式,如果传一个参数,加逗号,

from multiprocessing import Process
import os


def func(a,):
    print(a,os.getpid())

if __name__ == '__main__':
    P = Process(target=func,args=(1,))
    P.start()

daemon= True

将子进程更改为守护进程,特点是随着主进程的代码结束而结束。如果有守护进程或非守护进程,那么当主进程结束时,先结束守护进程,然后等待其他子进程结束后回收资源并关闭。

def func1():
    print('这是func1')
    time.sleep(0.5)

if __name__ == '__main__':
    P1 = Process(target=func1)
    P1.start()
    P1.daemon = True
    print('结束')

is_alive()

判断子进程是否开启,开启显示True,未开启显示False,如果结束子进程后立即查看子进程状态,那么还是会有可能出现True,因为系统还没来得及进行关闭进程。

def func1():
    print('这是func1')
    time.sleep(0.5)

if __name__ == '__main__':
    P1 = Process(target=func1)
    P1.start()
    print(P1.is_alive())
    print('结束')

join()

将异步非阻塞更改为阻塞状态,等待所有的子进程结束后,再将主进程向下执行。典型的同步阻塞状态。

from multiprocessing import Process
import time
# import join
import os



def func(a,):
    print(a,os.getpid())


if __name__ == '__main__':
    P = Process(target=func,args=(1,))
    P.start()
    P.join()
    print(2)

terminate()

结束进程,异步非阻塞状态。发送给计算机后,会继续执行后面的命令。

def func1():
    print('这是func1')
    time.sleep(0.5)


if __name__ == '__main__':
    P1 = Process(target=func1)
    P1.start()
    P1.terminate()
    print(P1.is_alive())
    print('结束')

在面向对象中创建进程:

class mult_process1(Process):        # Process必须继承
    def __init__(self,a,b):          #传参必须要继承一下Process中的init方法,然后再进行传参。
        super().__init__()
        self.a = a
        self.b = b
    def run(self):                  # run必须要写, start开启一个子进程,在子进程中调用run方法。(源码)
        print('这是run1')
        time.sleep(0.5)
class mult_process2(Process):        # Process必须继承
    def run(self):                  # run必须要写, start开启一个子进程,在子进程中调用run方法。(源码)
        print('这是run2')
        time.sleep(0.5)
if __name__ == '__main__':
    p1 = mult_process1(1,2)        #传参方式。
    p1.start()
    p2 = mult_process2()
    p2.start()

锁,Lock

当使用多进程或多并发的时候,需要处理同一个文件时,为了确保文件的正确和安全。当给一段代码加锁后,只允许一个用户进行文件操作,其他用户只能排队等待。

加锁的场景:

1. 操作共享的数据资源(文件,数据库)

2. 对资源进行修改。

3. 保证数据安全或不考虑有影响代码速率。

from multiprocessing import Process,Lock
import json
import time


def run(i,lock):
    with lock:                         #将整段代码进行加锁。相当于:头部加:lock.acquire() ,尾部加:lock.release()
        time.sleep(0.2)
        with open('log.log','r') as f:
            f = int(f.read())
            print(f'这是run{i},取了{f}')
            f-=1
        if f >= 0 :
            time.sleep(0.2)
            with open('log.log','w') as c:
                json.dump(f,c)

if __name__ == '__main__':
    lock = Lock()                               #实例化Lock
    for i in range(10):
        p1 = Process(target=run,args=(i,lock))  #将实例化后的lock传入子进程中。
        p1.start()

进程之间的通信,Queue

当主进程需要让子进程返回数据,并不占用主进程的向下业务时,就需要让子进程运行完成后,返回一个值。

def run(i,q):
    q.put(i+1)                             #put上传值

if __name__ == '__main__':
    q = Queue()                            #实例化
    p1 = Process(target=run,args=(8,q))    #传入子进程中
    p1.start()
    print(q.get())                         #获取值

注意点:

1. get(获取)或put(上传),会导致队列空或者队列满时(5)会发生阻塞情况。

2. 使用put_nowait和get_nowait不会阻塞,但超出或者空时会报错,可以使用try抓取报错来解决。

3. 队列使用socket(文件操作类)、pickle、lock来同时完成队列操作。相比于pipo(管道),要多了一个lock,更加的安全。

4. 使用nowait时会存在数据丢失现象,能不用尽量不使用。

from multiprocessing import Process,Queue      
import queue          #是抓取异常来使用的

def run(i,q):
    q.put_nowait(i+1)                            

if __name__ == '__main__':
    q = Queue()                            
    p1 = Process(target=run,args=(8,q))    
    p1.start()
    try:
        print(q.get_nowait())                         
    except queue.Empty:        #异常的报错是Empty和Full
        pass

实例: 

生产者消费者模型:

把一个生产数据,并且处理数据的过程进行解耦,让生产数据的过程和处理数据的过程达到一个工作效率上的平衡。将大程序分解成多个小功能进行处理。并使用队列来对内存队列进行控制。

import time
import random
from multiprocessing import Process,Queue


def producer(q):
    for i in range(1,10):
        time.sleep(random.random())
        print('制作了%s包子。'%(i))
        q.put(i)

def consumer(q,name):
    while True:
        time.sleep(3)
        q_get = q.get()
        if not q_get:break
        print('%s吃了第%s个包子'%(name,q_get))

def run(p,c):
    p_list = []
    for i in range(p):     #循环创建生产者
        p1 = Process(target=producer, args=(q,))
        p1.start()
        p_list.append(p1)
    for i2 in range(c):     #循环创建消费者
        Process(target=consumer, args=(q, 'xuan')).start()
    for i3 in p_list:       #等待生产者全部完成。
        i3.join()
    for i4 in range(c):     #判断消费者全部消费完后退出。
        q.put(None)
if __name__ == '__main__':
    q = Queue(3)                             #允许队列的数量,可以有效减少排队过多而导致的占用大量内存问题。
    run(3,1)

  

创建多个进程,输出子进程内容,在最后输出父进程。

from multiprocessing import Process
import time
import os

def func(a,):
    print(a,os.getpid())
    time.sleep(2)


if __name__ == '__main__':
    e = []
    for i in range(10):
        P = Process(target=func,args=(i,))
        P.start()
        e.append(P)                      #把子进程内存地址记住
    for i2 in e:i2.join()          #然后循环所有子进程,等待所有子进程的结束
    print('结束')

 

posted @ 2022-03-15 17:01  新兵蛋Z  阅读(319)  评论(0编辑  收藏  举报