Python进程、进程池以及进程间通信

概念

进程

  • 进程是系统进行资源分配和调度的基本单位,是操作系统结构的基础。简单可以理解为正在运行的程序的实例,都会有一个pid作为标识。

多任务

  • 以操作系统为例,在上面可以同时运行QQ word 微信等多个程序,这就是多任务
  • 之前的单核CPU也可以运行多任务,让任务交替执行,因为CPU执行效率很快,所以对用户是无感知的
  • 真正的多任务只能在多核CPU上完成,但如果任务数量大于CPU核心数量,操作系统也会自动把很多任务轮流调度到每个核心上执行

创建进程

fork创建进程

fork创建子进程
  • python中的os模块封装了常见的系统调用,其中包含fork,通过fork可以轻松的创建子进程
import os 
import time 

pid = os.fork()
print('pid is %s' % pid)
if pid < 0:
    print('fork调用失败')
elif pid == 0:
    # while True:
    #     print('this is 子进程')
    #     time.sleep(1)
    print('this is child process(%s) and parent is (%s)' % (os.getpid(), os.getppid()))
else:
    print('this is parent process(%s) and my child is (%s)' % (os.getpid(), pid))

print('父子进程都可以执行这里的代码')

输出结果为:

pid is 6661
this is parent process(6660) and my child is (6661)
父子进程都可以执行这里的代码
pid is 0
this is child process(6661) and parent is (6660)
父子进程都可以执行这里的代码
  • 在unix和Linux中,fork这个系统函数比较特殊,调用一次,返回两次。因为操作系统自动把当前进程复制一份,然后分别在父进程和子进程中返回
  • 当程序执行到fork时,操作系统会创建一个新的进程(子进程),然后复制父进程的所有信息到子进程中
  • 父进程和子进程都会从fork函数中得到一个返回值,在子进程中该值为0,父进程中则是子进程的ID
多进程修改全局变量
  • 多进程中,每个进程的变量(包括全局变量)都维系一份,互不影响
import os
import time

rac = os.fork()
num = 1
if rac == 0:
    time.sleep(1)
    num += 1
    print('---parent num = %d' % num)
else:
    num += 1
    time.sleep(1)
    print('---child num = %d' % num)

print('---end num = %d' % num)

输出结果为:

---child num = 2
---end num = 2
---parent num = 2
---end num = 2
多次fork
  • 每次fork都会创建一个子进程,并且后续代码每个进程都会执行到。
import os
import time

rac = os.fork()
if rac == 0:
    print('---demo1---')
else:
    print('---demo2---')

rac2 = os.fork()
if rac2 == 0:
    print('---demo3---')
else:
    print('---demo4---')

print('---end---')

输出结果为:

---demo2---
---demo4---
---end---
---demo3---
---end---
---demo1---
---demo4---
---end---
---demo3---
---end---
  • 如上所示,demo1 demo2各执行一次,demo3 demo4各执行两次,end累计执行四次
父子进程执行顺序
  • 执行顺序没有规律,取决于操作系统的调度算法

multiprocessing创建进程

上面提到的fork,只能在linux unix上调用,在Windows上是无法执行的,但python作为一个跨平台的语言,自然而然也提供了一个跨平台多进程的模块–multiprocessing

通过process创建进程
  • Process代表一个进程对象
  • 创建子进程时,只需要在构造函数内传入目标函数以及参数,调用start方法即可
  • join()方法是让主进程等待子进程结束后再执行后续代码
import os
from multiprocessing import Process
from time import sleep


def run_proc(name):
    print('子进程运行中,name = %s,pid = %d ...' % (name, os.getpid()))


"""
创建子进程时,只需要传入一个执行函数和函数的参数,创建一个Process实例,用start方法启动
join方法可以等待子进程结束后再往下运行,通常用于进程间的同步
"""
if __name__ == '__main__':
    print('父进程 %d.' % os.getpid())
    p = Process(target=run_proc, args=('test',))
    print('子进程将要执行')
    p.start()
    p.join()
    print('子进程执行完毕')

输出结果为:

父进程 6784.
子进程将要执行
子进程运行中,name = test,pid = 6785 ...
子进程执行完毕
通过Process子类创建进程
  • 创建进程还可以使用类的方式,自定义一个类,继承Process即可
import time
from multiprocessing import Process


class MyProcess(Process):
    def __init__(self, time, ):
        Process.__init__(self)
        self.time = time

    def run(self):
        # while True:
        print('---run---')
        # time.sleep(self.time)


if __name__ == '__main__':
    p = MyProcess(1)
    p.start()
    p.join()
    print('--main end--')

输出结果为:

---run---
--main end--

进程池Pool

  • 当创建的子进程数量不多时,可以通过Process来创建,如果要创建的子进程数量太多,那么就不适用这种方式了,需要使用进程池来解决。
  • 初始化一个Pool时,可以指定一个最大进程数,如果超过这个数,那么请求就会等待,直到池中有进程结束,才会创建新的进程来执行
from multiprocessing import Pool
import time, os, random


def worker(msg):
    t_start = time.time()
    print('%s 开始执行,进城号为 %d' % (msg, os.getpid()))
    # random.random()随机生成0-1的浮点数
    time.sleep(random.random() * 2)
    t_stop = time.time()
    print(msg, " 执行完毕,耗时%0.2f" % (t_stop - t_start))


# 创建一个进程池,里面最多三个进程
pool = Pool(3)
for i in range(0, 10):
    # 向进程池中添加人物
    # 如果人物的数量超过进程池大小的话,会等待进程池中有空闲的时候,自动添加进去
    pool.apply_async(worker, args=(i,))

print('---start---')
# 关闭进程池 关闭后po不再接受新的请求
pool.close()
# 等待po中所有子进程执行完成,必须放在close语句之后
# 如果没有join,会导致进程中的任务不会执行
pool.join()
print('---end---')

输出结果为

---start---
0 开始执行,进城号为 6804
1 开始执行,进城号为 6805
2 开始执行,进城号为 6803
0  执行完毕,耗时1.23
3 开始执行,进城号为 6804
2  执行完毕,耗时1.29
4 开始执行,进城号为 6803
1  执行完毕,耗时1.85
5 开始执行,进城号为 6805
4  执行完毕,耗时0.82
6 开始执行,进城号为 6803
3  执行完毕,耗时0.90
7 开始执行,进城号为 6804
6  执行完毕,耗时0.54
8 开始执行,进城号为 6803
5  执行完毕,耗时0.89
9 开始执行,进城号为 6805
7  执行完毕,耗时0.70
9  执行完毕,耗时0.92
8  执行完毕,耗时1.77
---end---
  • apply_async 异步非阻塞
  • apply 阻塞,上个进程结束才会执行下一个进程
  • close 关闭pool 不再接收请求
  • join 主进程阻塞,等待子进程运行结束,必须在closez以后

进程间通信

进程中的Queue
  • 进程间有时需要互相通信,操作系统提供了多种方式进行通信,Queue就是其中的一种
  • queue就是一个消息队列,提供了get() put() qsize() empty() full()等方法对队列进行操作
  • 下面以queue为例,创建两个进程,分别用来存储数据
from multiprocessing import Queue, Process
import random, time


# queue,实现多进程之间的数据传递,其实就是个消息队列

def write(q):
    for value in ['A', 'B', 'C']:
        print("put %s to queue" % value)
        q.put(value)
        time.sleep(random.random())


def read(q):
    while True:
        if not q.empty():
            value = q.get()
            print("Get value is %s" % value)
            time.sleep(random.random())
        else:
            break


if '__main__' == __name__:
    q = Queue()
    qw = Process(target=write, args=(q,))
    qr = Process(target=read, args=(q,))
    qw.start()
    qw.join()
    qr.start()
    qr.join()
    print('---end---')
    print(random.randint(2, 4))
    print(random.random())

输出结果为:

put A to queue
put B to queue
put C to queue
Get value is A
Get value is B
Get value is C
---end---
4
0.5260624608369131
进程池中的Queue
  • 如果使用进程池pool创建进程的话,就需要使用Manager().Queue()
from multiprocessing import Manager, Process, Pool
import threading
import random, time,os


# queue,实现多进程之间的数据传递,其实就是个消息队列

def write(q):
    print('---write thread is %s' % os.getpid())
    for value in ['A', 'B', 'C']:
        print("put %s to queue" % value)
        q.put(value)


def read(q):
    print('---read thread is %s' % os.getpid())
    for i in range(q.qsize()):
        print("Get value is %s" % q.get(True))


if '__main__' == __name__:
    print('---main thread is %s' % os.getpid())
    q = Manager().Queue()
    po = Pool()
    po.apply(write, args=(q,))
    po.apply(read, args=(q,))
    po.close()
    po.join()
    print('---end---')

输出结果为:

---main thread is 6907
---write thread is 6909
put A to queue
put B to queue
put C to queue
---read thread is 6910
Get value is A
Get value is B
Get value is C
---end---

总结

  • 本文主要介绍了进程概念 多任务 进程池 创建进程的几种方式以及进程间的通信等
  • fork 不建议使用,不支持跨平台
  • process
    • process函数形式
    • 继承Process,实现子类
  • pool
    • 进程池,当要创建的进程数量比较多时,建议使用该方式
  • queue
    • 进程间通信
    • 使用process创建的进程,使用Queue()
    • 使用进程池创建的进程通信,使用Manager().Queue()
  • 2
    点赞
  • 23
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
Python 中,可以使用 `multiprocessing` 模块中的 `Pool` 类来创建进程,以便于管理多个进程。具体来说,可以使用 `Pool` 类的 `map()` 方法来分配任务给进程中的进程,以并行地执行多个任务。 下面是一个示例,演示了如何在 Python 中开启进程: ```python import multiprocessing import time # 定义一个函数,用于模拟耗时任务 def task(num): print("Process {}: Start task {}".format(multiprocessing.current_process().name, num)) time.sleep(1) print("Process {}: End task {}".format(multiprocessing.current_process().name, num)) return num * num if __name__ == '__main__': # 创建一个进程,最大进程数为 2 pool = multiprocessing.Pool(processes=2) # 分配任务给进程处理,并获取结果 results = pool.map(task, range(5)) # 关闭进程 pool.close() pool.join() # 输出结果 print("Results: ", results) ``` 在上面的示例中,我们首先定义了一个函数 `task()`,用于模拟一个耗时任务。然后,我们使用 `multiprocessing.Pool()` 函数创建了一个最大进程数为 2 的进程,并使用 `pool.map()` 方法将任务分配给进程中的进程,以并行地执行多个任务。最后,我们输出了任务的执行结果。 需要注意的是,在使用进程时,需要将所有的函数、类定义等放到 `if __name__ == '__main__':` 语句块中,以避免在子进程中重复定义。同时,由于进程中的进程与主进程是独立的,因此不能通过共享内存等方式来进行数据交换,需要使用进程间通信机制,如消息队列、管道、套接字等,来进行数据传输。
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值