进程
Python中的多进程之间可以使用信号量和管道来进行同步通信。学过操作系统的知道,信号量是一种控制对公共资源或临界区的访问的机制,它维护着一个计数器,指定可同时访问资源或进入临界区的线程数。而管道则是在内存中开辟管道空间,生成管道对象,多个进程使用一个管道对象进行读写。
process进程
在Python中,“process”(进程)是指操作系统中执行中的一个程序实例。Python提供了多个模块来支持进程管理和并发执行。
multprocessing
提供了创建和管理进程的功能,可用于实现并发执行和利用多核处理器的能力。
from multiprocessing import Process
def my_function():
# 进程执行的代码
if __name__ == '__main__':
p = Process(target=my_function)
p.start() # 启动进程
p.join() # 等待进程结束
os
提供了与操作系统交互的功能,包括创建进程、执行系统命令等,但有些功能限制在linux系统中使用。
import os
pid = os.fork() # Linux创建子进程
if pid == 0:
# 子进程执行的代码
else:
# 父进程执行的代码
subprocess
用于创建和控制新的子进程,并与其进行交互。
import subprocess
result = subprocess.run(['ls', '-l'], capture_output=True, text=True)
print(result.stdout) # 子进程执行的输出
concurrent.futures
提供了高级的并发执行功能,包括线程池和进程池。
from concurrent.futures import ProcessPoolExecutor
def my_function():
# 进程池中的进程执行的代码
with ProcessPoolExecutor() as executor:
future = executor.submit(my_function)
result = future.result() # 获取进程执行的结果
信号量
当涉及到多线程访问共享资源时,确保线程安全是非常重要的。如果代码中存在竞争条件,多个线程可能会同时访问和修改共享数据,导致不一致的结果。python信号量(Semaphore)是一种用于控制多个线程或进程访问共享资源的同步机制。它允许多个线程同时访问共享资源,但限制同时访问的线程数量。
import threading
# 创建一个信号量,允许最多3个线程同时访问共享资源
semaphore = threading.Semaphore(3)
def worker():
# 获取信号量
semaphore.acquire()
try:
# 访问共享资源的代码
print("Worker {} is working.".format(threading.current_thread().name))
finally:
# 释放信号量
semaphore.release()
# 创建线程
threads = []
for i in range(5):
t = threading.Thread(target=worker, name=str(i))
threads.append(t)
t.start()
# 等待所有线程完成
for t in threads:
t.join()
管道
Python的multiprocessing模块提供了对管道的支持。在调用Pipe()函数创建管道时,会返回两个相关联的Connection对象,一个用于父进程(调用Pipe()的进程),一个用于子进程(通过os.fork()(Linux操作系统)或Process(win操作系统)创建的新进程)。这两个Connection对象之间建立了一个双向通信通道。
from multiprocessing import Process, Pipe
#两个进程需要执行的任务
def sender(conn):
# 发送数据到管道
data = [1, 2, 3, 4, 5]
conn.send(data)
conn.close()
def receiver(conn):
# 从管道接收数据
data = conn.recv()
print("Received data:", data)
conn.close()
if __name__ == '__main__':
# 创建管道绑定connection对象
parent_conn, child_conn = Pipe()
# 创建发送进程和接收进程并指定任务
sender_process = Process(target=sender, args=(child_conn,))
receiver_process = Process(target=receiver, args=(parent_conn,))
# 启动进程
sender_process.start()
receiver_process.start()
# 等待进程结束
sender_process.join()
receiver_process.join()
p.join() # 等待子进程结束
可以观察到子进程接受到父进程发出数据。
事件event
(Event类) python线程的事件用于主线程控制其他线程,事件是一个简单的线程同步对象,其提供几个主要方法:
方法 | 作用 |
---|---|
set() | 将事件的内部标志设置为True,表示事件已经发生。 |
clear() | 作用:将事件的内部标志设置为False,表示事件未发生。 |
wait(timeout=None) | 作用:等待事件的发生,直到事件被设置为True。 |
is_set() | 作用:检查事件的内部标志是否为True,即检查事件是否已经发生。 |
import threading
import time
#event做全局变量方便检测
event = threading.Event()
def lighter():
count = 0
event.set() # 初始值为绿灯
while True:
if 5 < count <= 10:
event.clear() # 红灯,清除标志位
print("\33[41;1mred light is on...\033[0m")
elif count > 10:
event.set() # 绿灯,设置标志位
count = 0
else:
print("\33[42;1mgreen light is on...\033[0m")
time.sleep(1)
count += 1
def car(name):
while True:
if event.is_set(): # 判断是否设置了标志位
print("[%s] running..." % name)
time.sleep(1)
else:
print("[%s] sees red light,waiting..." % name)
event.wait()
print("[%s] green light is on,start going..." % name)
light = threading.Thread(target=lighter, )
light.start()
car = threading.Thread(target=car, args=("BYD 仰望U8",))
car.start()#并发线程
实验可以看出对红绿灯有规律的模拟。
进程池
进程启动的开销比较大,使用多进程切换时会导致大量内存空间和时间消耗。为了防止这种情况发生可以使用进程池(由于启动线程的开销比较小,所以不需要线程池这种概念,多线程间的频繁得切换导致cpu时间开销增加系统处理变慢,并不会占用很多内存空间)。进程池是一种并发执行的模式,它允许您创建一组预先初始化的进程,然后通过将任务分配给这些进程的同时控制并发的数目或采取分批策略来控制开销。
对于Cpython而言,每个进程的每个线程只有一个可以持有GIL锁,所以不能做线程加速,但可以创建多个进程,每个进程都持有一个GIL锁,从而加速代码。
以下代码设置一个大小为4的进程池,可以看见,最大同时执行的进程只有四个。
import multiprocessing
import time
def worker(x):
# 这里是每个进程执行的具体任务
return x * x
time.sleep(5)
def main():
# 创建进程池,指定进程池的大小为4
pool = multiprocessing.Pool(processes=4)
# 向进程池提交任务
results = []
for i in range(10):
result = pool.apply_async(worker, (i,))
results.append(result)
# 监听进程池的状态
while True:
# 获取进程池中正在执行的任务数量
active_count = len(pool._pool)
# 计算空闲进程的数量
free_count = pool._processes - active_count
print(f"Active processes: {active_count}")
print(f"Free processes: {free_count}")
# 如果进程池已满,可以根据需要进行相应的处理
if active_count == pool._processes:
print("Process pool is full. Waiting for tasks to complete...")
# 检查所有任务是否已完成
all_completed = all(result.ready() for result in results)
if all_completed:
break
time.sleep(0.1)
# 关闭进程池
pool.close()
pool.join()
if __name__ == '__main__':
main()