Py学习  »  Python

Python多进程学习(一)

王大呀呀 • 4 年前 • 407 次点击  
阅读 7

Python多进程学习(一)

前情:这里只是单纯介绍 Python 中的多进程的程序开发,详细的内容还需要看操作系统知识。
复制代码

fork()

  • Python 的 os 模块封装了常见的系统调用,其中就包括 fork() ,可以在 Python 程序中轻松创建子进程,实现多进程。

  • Unix/Linux 操作系统(比如常见的 Mac 咯)提供了一个 fork() 系统调用,它被调用一次,返回两次,因为操作系统自动把当前进程(称为父进程)复制了一份(称为子进程),然后分别在父进程和子进程内返回。

  • 子进程永远返回0,而父进程返回子进程的 ID,因为一个父进程可以 fork 出很多子进程,所以父进程要记下每个子进程的 ID,而子进程只需要调用 getppid() 就可以拿到父进程的 ID。

实战(Python3环境)

import os
print("正在运行的进程是 %s ..." % os.getpid())
pid = os.fork() # 如果是子进程返回0,而父进程返回子进程的ID,有了fork调用,一个进程在接到新任务时就可以复制出一个子进程来处理新任务,常见的Apache服务器就是由父进程监听端口,每当有新的http请求时,就fork出子进程来处理新的http请求。
print("\rpid 是 %s" %pid)
if pid == 0:
    print("子进程是 %s 和父进程是 %s." % (os.getpid(), os.getppid()))
else:
    print("在父进程 %s 中创建了子进程 %s." % (os.getpid(), pid))
复制代码

运行结果:

正在运行的进程是 42322 ...
pid 是 46501
在父进程 42322 中创建了子进程 46501.
pid 是 0
子进程是 46501 和父进程是 42322.
复制代码

multiprocessing

multiprocessing 模块是跨平台版本的多进程模块。它提供了一个 Process 类来代表一个进程对象。所以不管在 Unix/Linux 操作系统,还是 Windows 操作系统,都可以用 Python 编写多进程的程序。

实战

from multiprocessing import Process
import os

# 子进程要执行的代码
def my_proc(name):
    print("运行子进程 %s." % (os.getpid()))
# 相当于程序执行入口
if __name__=='__main__':
    print("父进程 %s." %os.getpid())
    p = Process(target=my_proc, args=('test',))  # 传入需要执行的函数和函数需要的参数,用start()启动,这种方式比fork()更简单
    print("子进程将要开始.")
    p.start()
    p.join()  # join() 可以等待子进程结束后继续往下执行,通常用于进程间同步
    print("子进程结束.")
复制代码

运行结果:

父进程 42322.
子进程将要开始.
运行子进程 46580.
子进程结束.
复制代码

进程池 Pool

如果要启动大量的子进程,可以用进程池创建大量的子进程

实战

from multiprocessing import Pool
import os, time, random

def task(name):  
    print("运行任务 %s (%s)" %(name, os.getpid()))
    start = time.time()
    time.sleep(random.random() * 3)
    end = time.time()
    print("任务 %s 耗时 %.2f 秒" %(name, (end-start)))
    
if __name__ == "__main__":
    print("父进程 %s" %(os.getpid()))
    p = Pool(6)
    for i in range(10):
        p.apply_async(task, args=(i,))
    p.close()
    print("等待所有子进程完毕")
    p.join() # 对 Pool 对象调用 join() 方法会等待所有子进程执行完毕,调用 join() 之前必须先调用 close() ,调用 close() 之后就不能继续添加新的 Process 了
    print("所有子进程执行完毕")
复制代码

运行结果:

父进程 42322
运行任务 2 (43382)
运行任务 1 (43381)
运行任务 3 (43383)
运行任务 0 (43380)
运行任务 4 (43384)
运行任务 5 (43385)
任务 4 耗时 0.05 秒
运行任务 6 (43384)
等待所有子进程完毕
任务 5 耗时 0.26 秒
运行任务 7 (43385)
任务 1 耗时 0.57 秒
运行任务 8 (43381)
任务 7 耗时 0.96 秒
运行任务 9 (43385)
任务 8 耗时 1.03 秒
任务 3 耗时 2.00 秒
任务 0 耗时 2.07 秒
任务 6 耗时 2.26 秒
任务 2 耗时 2.70 秒
任务 9 耗时 2.61 秒
所有子进程执行完毕
复制代码

任务 2,1,3,0,4,5是立刻执行的,而其他任务要等待前面某个任务完成后才执行,这是因为 Pool 的大小设置为6,因此,最多同时执行6个进程,Pool 的默认大小是 CPU 的核数

子进程

很多时候,子进程并不是自身,而是一个外部进程。我们创建了子进程后,还需要控制子进程的输入和输出。subprocess 模块可以让我们非常方便地启动一个子进程,然后控制其输入和输出。

进程间通信

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

实战

from multiprocessing import Process, Queue
import os, time, random

# 往 queue 里写内容
def write(q):
    print("写内容的进程: %s" %os.getpid())
    for v in 'ABC':
        print("把 %s 写进队列..." %v)
        q.put(v)
        time.sleep(random.random())
        
# 从 queue 里读内容
def read(q):
    print("读内容的进程: %s" %os.getpid())
    while True:
        v = q.get(True)
        print("从队列中读 %s." %v)

if __name__=="__main__":
    q = Queue()
    pw = Process(target=write, args=(q,))
    pr = Process(target=read, args=(q,))
    pw.start()
    pr.start()
    pw.join() # 等待 pw 结束:
    pr.terminate() # pr 进程里是死循环,只能强行终止
复制代码

运行结果:

写内容的进程: 46049
读内容的进程: 46050
把 A 写进队列...
从队列中读 A.
把 B 写进队列...
从队列中读 B.
把 C 写进队列...
从队列中读 C.
复制代码

本文参考(不分先后顺序):

廖雪峰的官方网站:www.liaoxuefeng.com/wiki/101695…

希望看客老爷打赏些喝茶钱

支付宝

支付宝

微信

微信

Python社区是高质量的Python/Django开发社区
本文地址:http://www.python88.com/topic/35544
 
407 次点击