Python 进程

Posted 思考与践行

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Python 进程相关的知识,希望对你有一定的参考价值。

1. 进程

1.1 进程的创建 fork

  • 正在运行着的代码,就称为进程
# 示例:
import os

# 注意: fork 函数,只在 Unix/Linux/Mac 上运行, windows 不可以
pid = os.fork()
print(pid)
if pid == 0:
    # 子进程执行(pid==0)
    print("=== 子进程 === %d %d" % (os.getpid(),os.getppid()))
else:
    # 主(父)进程执行(pid>0)
    print("=== 父进程 ===%d" % os.getpid())

1.2 全局变量在进程中不共享

# 示例:
import os
import time

g_num = 100

ret = os.fork()
if ret == 0:
    print("=== process 1 ===")
    g_num += 1
    print("=== process 1  g_num = %d===" % g_num)
else:
    time.sleep(3)
    print("=== process 2 ===")
    print("=== process 2 g_num = %d ===" % g_num)

# 输出:
=== process 1 ===
=== process 1  g_num = 101 ===
=== process 2 ===
=== process 2  g_num = 100 ===

1.3 多次fork

# 示例:
import os

ret = os.fork()
if ret == 0:
    print("=== 1 ===")
else:
    print("=== 2 ===")

ret = os.fork()
if ret == 0:
    print("=== 3 ===")
else:
    print("=== 4 ===")

# 输出:
=== 2 ===
=== 4 ===
=== 1 ===
=== 3 ===
=== 4 ===
=== 3 ===

1.4 Process 创建子进程

# 示例
from multiprocessing import Process
import os

# 子进程要执行的代码
def run_proc(name):
    print('子进程运行中, name=%s, pid=%d...' % (name, os.getpid()))


if __name__=='__main__':
    print('父进程 %d.' % os.getpid())
    p = Process(target=run_proc, args=('test',))
    print('子进程将要执行')
    p.start()   # 创建子进程
    p.join()    # 等到子进程实例(p)执行结束后,才执行后面的代码
    print('子进程已结束')

1.5 Process 的子类

# 示例:
from multiprocessing import Process
import time
import os

# 继承Process类
class Process_Class(Process):
    # 因为Process类本身也有 __init__ 方法,这个类相当于重写了这个方法;
    # 但这样就会带来一个问题,我们并没有完全的初始化一个 Process 类, 所以就不能使用从这个类继承的一些方法
    # 最好的方法就是将继承类本身传递给 Process.__init__ 方法, 完成这些初始化操作
    def __init__(self, interval):
        Process.__init__(self)
        self.interval = interval

    # 重写Process类的run()方法
    def run(self):
        print("子进程(%s) 开始执行, 父进程为 (%s)" % (os.getpid(), os.getppid()))
        t_start = time.time()
        time.sleep(self.interval)
        t_stop = time.time()
        print("(%s)执行结束, 耗时%0.2f秒" % (os.getpid(), t_stop-t_start))

if __name__=="__main__":
    t_start = time.time()
    print("当前进程(%s)" % os.getpid())
    p1 = Process_Class(2)
    # 对一个不包含target属性的Process类执行start()方法,就会运行这个类中的 run() 方法
    p1.start()
    p1.join()
    t_stop = time.time()
    print("(%s)执行结束, 耗时%0.2f" % (os.getpid(), t_stop-t_start))

1.6 进程池 Pool(非阻塞方式)

# 示例:
from multiprocessing import Pool
import os, time, 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))

po = Pool(3)  # 定义一个进程池, 最大进程数 3
for i in range(0, 10):
    # Pool.apply_async(要调用的目标,(传递给目标的参数元组,))
    # 每次循环将会用空闲出来的子进程去调用目标
    po.apply_async(worker,(i,))

print("=== start ===")
po.close()  # 关闭进程池,关闭后po不再接收新的请求
po.join()   # 等待po中所有子进程执行完成, 必须放在close语句之后
print("=== close ===")

1.7 apply 阻塞方式添加任务

# 示例:
from multiprocessing import Pool
import os, time, 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))

po = Pool(3)  # 定义一个进程池, 最大进程数 3
for i in range(0, 10):
    po.apply(worker, (i,))

print("=== start ===")
po.close()
po.join()
print("=== close ===")

1.8 进程间通信(Queue)

# 示例一: Queue 存取数据
from multiprocessing import Queue
q=Queue(3)  # 初始化一个Queue对象,最多可接收三条put消息
q.put("消息1")
q.put("消息2")
print(q.full())     # False
q.put("消息3")
print(q.full())     # True

# 因为消息队列已下满,下面的try会抛出异常,第一个try会等待2秒后再抛出异常,第二个try会立刻抛出异常
try:
    q.put("消息4",True,2)
except:
    print("消息队列已满,现有消息数量: %s" % q.qsize())

try:
    q.put_nowait("消息4")
except:
    print("消息队列已满,现有消息数量: %s" % q.qsize())

# 推荐的方式: 先判断消息队列是否已满, 再写入
if not q.full():
    q.put_nowait("消息4")

# 读取消息时, 先判断消息队列是否为空,再读取
if not q.empty():
    for i in range(q.qsize()):
        print(q.get_nowait())



# 示例二: 进程间通信
# 在父进程中创建两个子进程,一个往Queue里写数据, 一个从Queue里读数据
from multiprocessing import Process, Queue
import os, time, random

# 写数据进程执行代码
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(True)
            print('Get %s from queue.' % value)
            time.sleep(random.random())
        else:
            break


if __name__=='__main__':
    # 父进程创建Queue,并传给各子进程
    q = Queue()
    pw = Process(target=write, args=(q,))
    pr = Process(target=read, args=(q,))
    # 启动子进程pw, 写入:
    pw.start()
    # 等待pw结束
    pw.join()
    # 启动子进程pr, 读取:
    pr.start()
    pr.join()
    # 所有子进程执行结束,输出
    print('所有数据都写入并读完')

1.9 进程池中的Queue

  • 在进程池中,需要使用 multiprocessing.Manager()中的 Queue();而不是 multiprocessing.Queue()
# 示例:
from multiprocessing import Manager, Pool
import os, time, random


# 写数据进程执行代码
def reader(q):
    print("reader启动(%s), 父进程为(%s)" % (os.getpid(), os.getppid()))
    for i in range(q.qsize()):
        print("reader从Queue获取到消息: %s" %q.get(True))

# 读数据进程执行代码:
def writer(q):
    print("writer启动(%s), 父进程为(%s)" % (os.getpid(), os.getppid()))
    for i in ['A', 'B', 'C']:
        q.put(i)

if __name__=="__main__":
    print("(%s) start" % os.getpid())
    q=Manager().Queue()     # 使用Manager中的Queue来初始化
    po=Pool()
    # 使用阻塞模式创建进程,这样就不需要在reader中使用死循环了, 可以让writer执行完成后,再用reader读取
    po.apply(writer,(q,))
    po.apply(reader,(q,))
    po.close()
    po.join()
    print("(%s) End" % os.getpid())


参考资料:

以上是关于Python 进程的主要内容,如果未能解决你的问题,请参考以下文章

[Python3] 043 多线程 简介

java 简单的代码片段,展示如何将javaagent附加到运行JVM进程

代码片段:Shell脚本实现重复执行和多进程

python中的多线程和多进程编程

使用 asyncio 将 bash 作为 Python 的子进程运行,但 bash 提示被延迟

LINUX PID 1和SYSTEMD PID 0 是内核的一部分,主要用于内进换页,内核初始化的最后一步就是启动 init 进程。这个进程是系统的第一个进程,PID 为 1,又叫超级进程(代码片段