Python学习笔记四:多进程

多进程

fork()

Unix/Linux/Mac操作系统都可以使用fork()函数来创建子进程,分别在父进程和子进程内返回

os.fork() 会有两次返回值,分别是父进程和子进程的返回值
在父进程中,fork返回的值是子进程的PID;
子进程中,这个返回值为0
子进程应该是从调用fork()的地方开始执行代码

父子进程并不能确定执行顺序

import os  #  导入os模块

print ('当前进程的ID是:%s' % os.getpid()) # os.getpid()返回的是进程的id不是线程

ID = os.fork()  # 创建子进程,并返回进程的id,父进程返回的是子进程的id,子进程返回的是0

if ID == 0:
    print ('这是子进程,ID是:%s。。父进程ID是:%s' % (os.getpid(), os.getppid()))
else:
    print ('这是父进程,ID是:%s' % os.getpid())

结果:

当前进程的ID是:1064
这是父进程,ID是:1064
这是子进程,ID是:1065。。父进程ID是:1064


multiprocessing

multiprocessing模块就是跨平台版本的多进程模块。

multiprocessing模块提供了一个Process类来代表一个进程对象,创建子进程时,只需要传入一个执行函数和函数的参数,创建一个Process实例,用start()方法启动

Process类

1.1 构造方法

def __init__(self, group=None, target=None, name=None, args=(), kwargs={})

group:进程所属组,基本不用 
target:进程调用对象(可以是一个函数名,也可以是一个可调用的对象(实现了__call__方法的类)) 
args:调用对象的位置参数元组 
name:别名 
kwargs:调用对象的关键字参数字典

1.2 实例方法

is_alive():返回进程是否在运行 
start():启动进程,等待CPU调度 
join([timeout]):阻塞当前上下文环境,直到调用此方法的进程终止或者到达指定timeout 
terminate():不管任务是否完成,立即停止该进程 
run():start()调用该方法,当实例进程没有传入target参数,stat()将执行默认的run()方法

1.3 属性

authkey: 
daemon:守护进程标识,在start()调用之前可以对其进行修改 
exitcode:进程的退出状态码 
name:进程名 
pid:进程



from multiprocessing import Process
import os

def run_proc(name):
    print('Run child process %s (%s)...' % (name, os.getpid()))

if __name__=='__main__':
    print('Parent process %s.' % os.getpid())
    p = Process(target=run_proc, args=('test',))
    print('Child process will start.')
    p.start()
    p.join()
    print('Child process end.')
"C:\Program Files (x86)\Python36-32\python.exe" D:/python_work/PythonLearning/process.py
Parent process 2432.
Child process will start.
Run child process test (8992)...
Child process end.

Process finished with exit code 0

Pool

from multiprocessing import Pool
import os, time, random

def long_time_task(name):
    print('Run task %s (%s)...' % (name, os.getpid()))
    start = time.time()
    time.sleep(random.random() * 3)
    end = time.time()
    print('Task %s runs %0.2f seconds.' % (name, (end - start)))

if __name__=='__main__':
    print('Parent process %s.' % os.getpid())
    p = Pool(4)
    for i in range(5):
        p.apply_async(long_time_task, args=(i,))
    print('Waiting for all subprocesses done...')
    p.close()
    p.join()
    print('All subprocesses done.')
"C:\Program Files (x86)\Python36-32\python.exe" D:/python_work/PythonLearning/process.py
Parent process 8216.
Waiting for all subprocesses done...
Run task 0 (2884)...
Run task 1 (3572)...
Run task 2 (1428)...
Run task 3 (5964)...
Task 0 runs 1.24 seconds.
Run task 4 (2884)...
Task 3 runs 1.65 seconds.
Task 1 runs 1.82 seconds.
Task 2 runs 1.98 seconds.
Task 4 runs 0.91 seconds.
All subprocesses done.


join

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

#encoding:utf-8
from multiprocessing import Process
import os, time, random

def r1(process_name):
    for i in range(5):
        print process_name, os.getpid()     #打印出当前进程的id
        time.sleep(random.random())
def r2(process_name):
    for i in range(5):
        print process_name, os.getpid()     #打印出当前进程的id
        time.sleep(random.random()*2)

if __name__ == "__main__":
        print "main process run..."
        p1 = Process(target=r1, args=('process_name1', )) 
        p2 = Process(target=r2, args=('process_name2', )) 

        p1.start()
        p2.start()
        p1.join()
        #p2.join()    
        print "main process runned all lines..."

发现主线程只是等待p1完成了,就会向下执行,而不会等待p2是否完成。

所以使用多进程的常规方法是,先依次调用start启动进程,再依次调用join要求主进程等待子进程的结束。


进程通信

Process之间肯定是需要通信的,操作系统提供了很多机制来实现进程间的通信。Python的multiprocessing模块包装了底层的机制,提供了QueuePipes等多种方式来交换数据。

Queue:

Queue是python标准库中的线程安全的队列(FIFO)实现,提供了一个适用于多线程编程的先进先出的数据结构,即队列,用来在生产者和消费者线程之间的信息传递

class Queue.Queue(maxsize=0)

FIFO即First in First Out,先进先出。Queue提供了一个基本的FIFO容器,使用方法很简单,maxsize是个整数,指明了队列中能存放的数据个数的上限。一旦达到上限,插入会导致阻塞,直到队列中的数据被消费掉。如果maxsize小于或者等于0,队列大小没有限制。

class Queue.LifoQueue(maxsize=0)

LIFO即Last in First Out,后进先出。与栈的类似,使用也很简单,maxsize用法同上

class Queue.PriorityQueue(maxsize=0)

构造一个优先队列。maxsize用法同上。

put(item[, block[, timeout]])

将item放入队列中。

  1. 如果可选的参数block为True且timeout为空对象(默认的情况,阻塞调用,无超时)。
  2. 如果timeout是个正整数,阻塞调用进程最多timeout秒,如果一直无空空间可用,抛出Full异常(带超时的阻塞调用)。
  3. 如果block为False,如果有空闲空间可用将数据放入队列,否则立即抛出Full异常

其非阻塞版本为put_nowait等同于put(item, False)

get([block[, timeout]])

从队列中移除并返回一个数据。block跟timeout参数同put方法

其非阻塞方法为`get_nowait()`相当与get(False)

empty()

如果队列为空,返回True,反之返回False


from multiprocessing import Process, Queue
import os, time, random
# queue模块实现了多生产者,多消费者的队列。当 要求信息必须在多线程间安全交换,这个模块在 线程编程时非常有用 。
# Queue模块实现了所有要求的锁机制。
# 说了半天就是Queue模块主要是多线程,保证线程安全使用的。
#  这个类实现了三种类型的queue,区别仅仅在于进去和取出的位置。在一个FIFO(First In,First Out)队列中,先加先取。
# 在一个LIFO(Last In First Out)的队列中,最后加的先出来(操作起来跟stack一样)。priority队列,有序保存,优先级最低的先出来
# class queue.Queue(maxsize = 0)构造一个FIFO队列
#Queue.get(item, block=True, timeout=None): 从队列里取数据。
# 如果为空的话,blocking = False 直接报 empty异常。
# 如果blocking = True,就是等一会,timeout必须为 0 或正数。
# None为一直等下去,0为不等,正数n为等待n秒还不能读取,报empty异常。
#
# 写数据进程执行的代码:
def write(q):
    print('Process to write: %s' % os.getpid())
    for value in ['A', 'B', 'C']:
        print('Put %s to queue...' % value)
        q.put(value)
        time.sleep(2)

# 读数据进程执行的代码:
def read(q):
    print('Process to read: %s' % os.getpid())
    while True:
        time.sleep(3)
        value = q.get(block=True, timeout=None)
        print('Get %s from queue.' % value)

if __name__=='__main__':
    # 父进程创建Queue,并传给各个子进程:
    q = Queue()
    pw = Process(target=write, args=(q,))
    pr = Process(target=read, args=(q,))
    # 启动子进程pw,写入:
    pw.start()
    # 启动子进程pr,读取:
    pr.start()
    # 等待pw结束:
    pw.join()
    # pr进程里是死循环,无法等待其结束,只能强行终止:
    pr.terminate()
"C:\Program Files (x86)\Python36-32\python.exe" D:/python_work/PythonLearning/process.py
Process to write: 4940
Put A to queue...
Process to read: 8208
Put B to queue...
Get A from queue.
Put C to queue...
Get B from queue.

multiprocessing.Pipe([duplex]) 
返回2个连接对象(conn1, conn2),代表管道的两端,默认是双向通信.如果duplex=False,conn1只能用来接收消息,conn2只能用来发送消息.不同于os.open之处在于os.pipe()返回2个文件描述符(r, w),表示可读的和可写的


# -*- coding:utf-8 -*-
from multiprocessing import Process, Pipe

def proc1(pipe):
    s = 'Hello,This is proc1'
    pipe.send(s)

def proc2(pipe):
    while True:
        print("proc2 recieve:"+ pipe.recv())

if __name__ == "__main__":
    pipe = Pipe()
    p1 = Process(target=proc1, args=(pipe[0],))
    p2 = Process(target=proc2, args=(pipe[1],))
    p1.start()
    p2.start()
    p1.join()
    p2.join(2)   #限制执行时间最多为2秒
    print('\nend all processes.')
    p2.terminate()
"C:\Program Files (x86)\Python36-32\python.exe" D:/python_work/PythonLearning/processpipe.py
proc2 recieve:Hello,This is proc1

end all processes.

Process finished with exit code 0


猜你喜欢

转载自blog.csdn.net/yaoliuwei1426/article/details/80776794
今日推荐