Python 多进程的使用

一、普通多进程

import time
import multiprocessing

def worker(flag):
    print("before sleep---%s" % flag)
    time.sleep(3)
    print("after sleep---%s" % flag)

if __name__ == '__main__':
    p_one = multiprocessing.Process(target=worker, args=("one",))
    p_two = multiprocessing.Process(target=worker, args=("two",))

    p_one.start()
    p_two.start()

输出结果:

before sleep—one
before sleep—two
after sleep—one
after sleep—two

二、守护进程

通过 daemon 来实现,当 darmon 为 True 时,当主进程结束时,子进程也会被强制结束

import time
import multiprocessing

def worker(flag):
    print("before sleep---%s" % flag)
    time.sleep(3)
    print("after sleep---%s" % flag)

if __name__ == '__main__':
    p_one = multiprocessing.Process(target=worker, args=("one",))
    p_two = multiprocessing.Process(target=worker, args=("two",))
    # 在主进程结束后,p_one 也将结束,所以不会有 after sleep---one
    p_one.daemon = True  # or False
    p_one.start()
    p_two.start()
    # 主进程等待1秒结束,等待打印 before sleep 结束
    time.sleep(1)

输出结果:

before sleep—one
before sleep—two
after sleep—two

三、进程锁

通过 multiprocessing.Lock() 来实现,在执行操作前通过 acquire() 加锁,结束后通过 release() 来释放锁。也可以使用 with 语句避免显示加锁。

import time
import multiprocessing

def worker_one(lock, flag):
    # 显式加锁
    lock.acquire()
    print("one 加锁")
    time.sleep(3)
    print("one 释放")
    # 释放锁
    lock.release()


def worker_two(lock, flag):
    # 通过 with 使用锁,不用手动释放
    with lock:
        print("two 加锁")
        time.sleep(3)
        print("two 释放")

if __name__ == '__main__':
    # 声明锁
    lock = multiprocessing.Lock()

    p_one = multiprocessing.Process(target=worker_one, args=(lock, 1))
    p_two = multiprocessing.Process(target=worker_two, args=(lock, 2))

    p_one.start()
    p_two.start()

输出结果:

one 加锁
… 等待一会
one 释放
two 加锁
… 等待一会
two 释放

四、信号量

可以通过 Semaphore 来实现对有限资源的使用控制,避免同同时启动太多进程跑崩系统。(进程池的概念)

import time
import multiprocessing

def worker_pool(semaphore, flag):
    semaphore.acquire()
    print("I am %d" % flag)
    time.sleep(3)
    semaphore.release()

if __name__ == '__main__':
    semaphore = multiprocessing.Semaphore(2)
    n = 0
    while n < 6:
        p = multiprocessing.Process(target=worker_pool, args=(semaphore, n))
        p.start()
        n += 1
    print('主进程结束了~')

输出结果:每两行之间会间隔大约三秒

I am 0
I am 1
I am 2
I am 3
I am 4
I am 5

五、进程间通信

通过事件(Event)来实现进程间的通信。主要用于主线程控制其他线程的执行,有点类似 Golang 中的 channel。事件主要提供了四个方法 set、wait、clear、is_set。

常用方法
  • .set() 设置事件为 True,用在控制器处。
  • .wait(timeout=Nont) 等待事件为 True,用在监控状态执行处。其接受一个 int 型参数(超时事件,单位为秒)。
  • .cleat() 将事件设置为False,所有 wait 态接收到 False 结束。
  • .is_set() 检查事件状态,返回 True 或 False。
import time
import multiprocessing


def worker(e):
    print("worker: starting")
    e.wait()
    print("worker: .is_set():%s" % e.is_set())


def worker_for_timeout(e):
    print("worker_for_timeout:starting")
    e.wait(2)
    print("wait_for_event_timeout: .is_set():%s" % e.is_set())


if __name__ == "__main__":
    e = multiprocessing.Event()
    w1 = multiprocessing.Process(target=worker, args=(e,))

    w2 = multiprocessing.Process(target=worker_for_timeout, args=(e,))
    w1.start()
    w2.start()

    time.sleep(3)
    # 在三秒后设置事件 所有等待两秒的或超时
    e.set()
    print("Main e.set()")

输出结果:

worker: starting
worker_for_timeout:starting
wait_for_event_timeout: .is_set():False
Main e.set()
worker: .is_set():True

六、队列

Queue是多进程安全的队列,可以使用 Queue 实现多进程之间的数据传递。其通过 .put() 向队列中添加数据,.get() 方法从队列中取走数据。.put() 和 .get() 都接收两个参数 timeout、blocked,若 blocked 为 False 或 blocked 为 True 且等待 timeout 时间后,队列空一直没有成功从队列中取出数据 或 队列满一直没成功将数据加入到队列中,会抛出 Queue.Empty 或 Queue.Full 异常。

import multiprocessing


def process_push(q):
    q.put(1, block=False)


def process_pull(q):
    print(q.get(block=True, timeout=3))


if __name__ == "__main__":
    q = multiprocessing.Queue()
    reader = multiprocessing.Process(target=process_pull, args=(q,))
    reader.start()

    writer = multiprocessing.Process(target=process_push, args=(q,))
    writer.start()

    reader.join()
    writer.join()

输出结果:

1

七、Pipe

Pipe 类似于一个双端队列,在声明时接收一个 duplex 参数,返回一个数组(两个 Connection 实例),当duplex 为 False 时,只有 Connection0 可以接收,Connection1 负责发送,当 duplex 为 True 时,Connection0 和 Connection1 都可以接收和发送。

对于 Connection 对象有两个常用方法:

  • .send():其接收一个参数,为发送到 Pipe 中的数据。
  • .recv(): received,从队列中取数据,当 Pipe 中没有数据时会一直阻塞,如果 Pipe 已经关闭,将会抛出异常。
import multiprocessing
import time


def worker_one(pipe):
    while True:
        print("worker_one rev:", pipe.recv())
        time.sleep(1)


def worker_two(pipe):
    for i in range(5):
        print("worker_two send: %s" % (i))
        pipe.send(i)
        time.sleep(1)


if __name__ == "__main__":
    pipe = multiprocessing.Pipe(duplex=False)
    p1 = multiprocessing.Process(target=worker_one, args=(pipe[0],))
    p2 = multiprocessing.Process(target=worker_two, args=(pipe[1],))

    p1.start()
    p2.start()

    p1.join()
    p2.join()

输出结果:

worker_two send: 0
worker_one rev: 0
worker_two send: 1
worker_one rev: 1
worker_two send: 2
worker_one rev: 2
worker_two send: 3
worker_one rev: 3
worker_two send: 4
worker_one rev: 4

八、进程池

通过 multiprocessing.Pool 来实现进程池。在声明池时其接受一个 int 型数据来确定池的大小。

常用方法
  • .apply() 阻塞式添加任务,任务顺序执行
  • .apply_async() 非阻塞式添加任务,任务顺序执行
  • .close() 关闭池,不可再想池中添加任务
  • .join() 等待池中的所有任务执行完毕
  • .terminate() 强制关闭池,所有进程结束
import time
import multiprocessing

def worker(flag):
    print("before sleep---%s" % flag)
    time.sleep(3)
    print("after sleep---%s" % flag)
    return flag

if __name__ == '__main__':
    # 进程池
    pool = multiprocessing.Pool(processes=3)
    result = []
    for i in range(4):
        # 非阻塞的,池中的所有进程并行执行, 返回 ApplyResult 实例,可以通过 .get() 获取结果
        # pool.apply_async(worker, (i,))
        # 阻塞式添加,池中的进程顺序执行,返回任务的 return 值
        # pool.apply(worker, (i,))
        # 进程执行结束后会返回结果给主进程    
        result.append(pool.apply_async(worker, (i,)))

    # 关闭池,不允许在添加
    pool.close()
    # 等待池中所有进程执行完毕
    pool.join()

    # 遍历所有结果集 pool.ApplyResult
    for i in result:
        print("结果集 %s" % i.get())

输出结果:

before sleep—0
before sleep—1
before sleep—2
after sleep—0
after sleep—1
after sleep—2
before sleep—3
after sleep—3
结果集 0
结果集 1
结果集 2
结果集 3

好了,关于 Python 进程的操作到这就介绍完了。你可以看一下其他文章,了解一下 Python 的线程及线程与 GIL 的关系。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值