多进程

多任务编程——进程

什么是进程:

操作系统在运行过程中,一个程序运行起来就是一个进程。在Python中,多进程编程可以让我们的程序运行效率更高。同一时刻可以做更多的事情。因此多进程编程显得十分重要。

multiprocessing模块介绍:

multiprocessingPython中一个专门用来创建创建多进程的库,multiprocessing提供了一个Process类来创建进程。以下用一个例子讲解Process类的使用:

from multiprocessing import Process
import time


def test():
    print('test process')

if __name__ == '__main__':
    # 开启一个子进程
    p = Process(target=test)
    p.start()

    while True:
        print('main process')
        time.sleep(1)

获取进程号:

Python中可以通过os模块的一个函数getpid()可以获取到当前进程的进程号。通过getppid()可以获取到这个进程的父进程的进程号。示例代码如下:

from multiprocessing import Process
import os

def test():
    print('===子进程===')
    print('子进程id:',os.getpid())
    print('父进程id:',os.getppid())
    print('===子进程===')

if __name__ == '__main__':
    p = Process(target=test)
    p.start()
    print('主进程id:',os.getpid())

父进程会等待子进程执行完毕后再退出:

如果在父进程中执行完所有代码后,还有子进程在执行,那么父进程会等待子进程执行完所有代码后再退出。

Process对象的join方法:

使用Process创建了子进程,调用start方法后,父子进程会在各自的进程中不断的执行代码。有时候如果想等待子进程执行完毕后再执行下面的代码,那么这时候可以调用join方法。示例代码如下:

from multiprocessing import Process

def test():
    for x in range(5):
        print('子进程中的代码%s' % x)

if __name__ == '__main__':
    p = Process(target=test)
    p.start()
    # 主进程阻塞,等待子进程完成才执行下面的代码
    p.join()
    print('父进程中的代码')

join方法还可以传一个timeout,用来指定等待的最长时间。示例代码如下:

from multiprocessing import Process
import time

def test(value):
    for x in range(5):
        print('子进程中的代码%s' % x)
        time.sleep(1)

if __name__ == '__main__':
    p = Process(target=test,args=(1,))
    p.start()
    # 主进程阻塞,等待子进程完成才执行下面的代码
    p.join(2)
    print('父进程中的代码')

使用类的方式创建子进程:

有些时候,你想以类的形式定义子进程的执行代码。那么你可以自定义一个类,让他继承自Process,然后在这个类中实现run方法,以后这个子进程在执行的时候就会调用这个run方法中的代码。示例代码如下:

from multiprocessing import Process
import os
import time

class MyProcess(Process):
    def run(self):
        for x in range(5):
            print('子进程:%s'%x)
            time.sleep(1)

if __name__ == '__main__':
    p = MyProcess()
    p.start()
    print('主进程id:',os.getpid())

进程池:

multiprocessing模块中有一个类Pool,这个类相当于一个池,专门用来存储进程。Pool__init__可以传递一个参数,这个参数指定这个进程池中同一时刻最多只能拥有多少个进程。并且,在使用进程池,父进程不会等待子进程池中的子进程执行完毕后退出,而是当父进程中的代码执行完毕以后会立即退出。相关的示例代码如下:

from multiprocessing import Process,Pool
import os
import time

def worker(num):
    for x in range(5):
        print('num:%s,pid:%s' % (num, os.getpid()))
        time.sleep(1)

if __name__ == '__main__':
    # 这个池子中同一时刻最多只能有3个进程
    pool = Pool(3)
    for x in range(10):
        pool.apply_async(worker,(x,))

    # 关闭进程池,不能再添加新进程了
    pool.close()
    # 主进程把子进程添加到进程池中后,不会等待进程池中其他的子进程都执行完毕后再退出,
    # 而是当主进程的代码执行完毕后会立刻退出,因此如果这个地方没有join,那么子进程
    # 将得不到执行
    pool.join()

进程间数据不共享:

在一个程序中,如果创建了一个子进程,那么这个子进程会拷贝一份当前进程所有的资源作为子进程的运行环境。也就是说,子进程中的变量可能跟父进程一样,但其实是另外一个块内存区域了。他们之间的数据是不共享的。以下代码演示一下:

from multiprocessing import Process

AGE = 1

def test(names):
    global AGE
    AGE += 1
    names.append('ketang')
    print('=====子进程=====')
    print('AGE:%d,id:%s' % (AGE,id(AGE)))
    print('names:%s' %names)
    print('=====子进程=====')

if __name__ == '__main__':
    names = ['test']
    p = Process(target=test,args=(names,))
    p.start()
    p.join()

    print('=====父进程=====')
    print('AGE:%d,id:%s' % (AGE, id(AGE)))
    print('names:%s' % names)
    print('=====父进程=====')

Queue消息队列:

进程之间数据都是不共享的,因此想要在两个进程之间使用相同的数据,那么这时候就需要使用进程间的通信,进程间通信有多种方式。两种,第一种是管道(Pipe)、第二种是队列(Queue)。以下讲解下使用Queue的通信。

首先讲解下Queue的一些常用方法的:

  1. Queue(n):初始化一个消息队列,并指定这个队列中最多能够容纳多少条消息。
  2. put(obj,[block[,timeout]]):推入一条消息到这个队列中。默认是阻塞的,也就是说如果这个消息队列中已经满了,那么会会一直等待,将这个消息添加到消息队列中。timeout可以指定这个阻塞最长的时间,如果超过这个时间还是满的,就会抛出异常。
  3. put_nowait() :非阻塞的推入一条消息,如果这个队列已经满了,那么会立马抛出异常。
  4. qsize():获取这个消息队列消息的数量。
  5. full():判断这个消息队列是否满了。
  6. empty():判断这个消息队列是否空了。
  7. get([block[,timeout]]):获取队列中的一条消息,然后将其从队列中移除,block默认为True。如果设置blockFalse,那么如果没值,会立马抛出异常。timeout指定如果多久没有获取到值后会抛出异常。

相关示例代码如下:

# 初始化一个Queue对象,最多只能存放三条消息
q = Queue(3)

# 存放第一条消息
q.put('m1')
# 存放第二条消息
q.put('m2')

# 判断这个队列中是否已经满了
print(q.full())

# 存放第三条消息
q.put('m3')

# 判断这个队列中的消息是否已经满了
print(q.full())

# 因为如果消息队列已经满了,那么再put进去的时候就会报错
try:
    q.put_nowait('m4')
except:
    print('消息队列已经满了,现有消息数量:%s' % q.qsize())

print(q.get())

使用Queue给Process进程做进程间通信:

from multiprocessing import Process,Queue
import time

def read(q):
    while not q.empty():
        # 以阻塞的方式
        value = q.get(True)
        print('read value %s' % value)
        time.sleep(1)

def write(q):
    for x in ['m1','m2','m3']:
        print('write value %s' % x)
        q.put(x)
        time.sleep(1)


if __name__ == '__main__':
    q = Queue()
    pw = Process(target=write,args=(q,))
    pr = Process(target=read,args=(q,))

    pw.start()
    pw.join()
    pr.start()

使用QueuePool进程池做进程间通信:

如果要使用Pool创建进程,就需要使用multiprocessing.Manager()中的Queue(),而不是multiprocessing.Queue(),否则会报错。RuntimeError: Queue objects should only be shared between processes through inheritance
下面代码演示进程池中的进程通信:

from multiprocessing import Pool,Queue,Process,Manager

def read(q):
    while not q.empty():
        print('读到的值:%s' % q.get())

def write(q):
    for x in ['m1','m2','m3']:
        q.put(x)

if __name__ == '__main__':
    q = Manager().Queue(3)
    pool = Pool(2)
    pool.apply(write,args=(q,))
    pool.apply(read,args=(q,))
    pool.close()

    pool.join()

 

posted @ 2021-03-12 13:08  大碗炸酱面  阅读(63)  评论(0编辑  收藏  举报