一、普通多进程
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 的关系。