【一】进程间通信
(1)什么是进程间通信
- 进程间通信(Inter-Process Communication,IPC)是指在不同的进程之间传递数据或信息的机制。
(2)如何实现进程间通信
- 借助于消息队列,进程可以将消息放入队列中,然后由另一个进程从队列中取出。
- 这种通信方式是非阻塞的,即发送进程不需要等待接收进程的响应即可继续执行。
- multiprocessing模块支持两种形式:队列和管道,这两种方式都是使用消息传递的
(3)什么是管道
- 管道是一种半双工的通信机制,即只能在一个方向上进行数据传输。
- 子进程可以通过继承父进程的管道来实现通信。
- stdin、stdout和stderr是Python中的三个内置文件对象,它们分别代表标准输入、标准输出和标准错误。
- 这些对象也可以作为管道使用。
- 当我们在一个进程中使用read方法读取管道内的消息后,其他进程将无法再获取该管道内的任何其他消息。
- 因此,我们需要使用锁或其他同步机制来确保多个进程能够正确地访问和修改共享资源。
(4)什么是队列
- 队列是一种线程安全的数据结构,它支持在多线程环境中高效地实现生产者-消费者模型。
- 队列的特性是先进先出(First-In-First-Out, FIFO),即先插入队列的数据将先被取出。
- 堆栈是一种后进先出(Last-In-First-Out, LIFO)的数据结构,与队列相反,最后插入的数据将首先被取出。
(5)进程间通信的目的
(6)子进程与主进程之间通信
- 由于主进程和子进程之间是独立运行的,各自拥有独立的内存空间,它们在往队列中放入消息时,并不会相互影响。
| from multiprocessing import Process, Queue |
| |
| |
| def producer(queue_obj): |
| queue_obj.put('我是子进程,嘿嘿') |
| |
| |
| if __name__ == '__main__': |
| queue_obj = Queue(5) |
| |
| |
| p = Process(target=producer,args=(queue_obj,)) |
| p.start() |
| p.join() |
| print(queue_obj.get()) |
(7)子进程与子进程之间通信
- 通过queue实现了子进程和子进程之间的通信,其中主进程通过队列向子进程发送了一条消息,子进程接收并打印了这条消息。
| from multiprocessing import Process, Queue |
| |
| |
| def boss(queue_obj): |
| |
| queue_obj.put("好好上班!") |
| |
| |
| def employee(queue_obj): |
| msg = queue_obj.get() |
| print(f'老板发来一条消息:>>>{msg}') |
| |
| |
| if __name__ == '__main__': |
| queue_obj = Queue(5) |
| |
| |
| task_boss = Process(target=boss, args=(queue_obj,)) |
| task_employee = Process(target=employee, args=(queue_obj,)) |
| |
| |
| task_boss.start() |
| task_employee.start() |
| |
| |
| task_boss.join() |
| task_employee.join() |
(8)生产者和消费者模型
- 生产者-消费者模型是一种常见的并发编程模型,其中有两类并发执行的任务:生产者和消费者。生产者负责生成数据,而消费者负责处理和消耗这些数据。它们通过共享的缓冲区(通常是一个队列)进行通信,以实现解耦和异步操作。
(1)消费者大于生产者
| from multiprocessing import Process, Queue |
| import random |
| import time |
| |
| |
| def producer(name, food, queue_obj): |
| for i in range(1, 5): |
| data = f"厨师{name} 生产出了第{i}道 {food}" |
| time.sleep(random.randint(1, 3)) |
| |
| queue_obj.put(data) |
| |
| |
| def customer(name, queue_obj): |
| while True: |
| from_producer_food = queue_obj.get() |
| time.sleep(random.randint(1, 2)) |
| print(f"客人 {name} 吃了{from_producer_food}") |
| |
| |
| def main(): |
| task_list = [] |
| |
| queue_obj = Queue() |
| |
| producer_heart = Process(target=producer, args=('heart', '小炒肉', queue_obj)) |
| producer_god = Process(target=producer, args=('god', '酸菜鱼', queue_obj)) |
| |
| |
| customer_pp = Process(target=customer, args=('pp', queue_obj)) |
| customer_godfather = Process(target=customer, args=('godfather', queue_obj)) |
| |
| task_list.append(producer_heart) |
| task_list.append(producer_god) |
| task_list.append(customer_pp) |
| task_list.append(customer_godfather) |
| |
| |
| task_list_new = [] |
| for task in task_list: |
| task.start() |
| task_list_new.append(task) |
| for task in task_list_new: |
| task.join() |
| |
| |
| if __name__ == '__main__': |
| main() |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
(2)为生产者添加结束标志
| from multiprocessing import Process, Queue |
| import random |
| import time |
| |
| |
| def producer(name, food, queue_obj): |
| for i in range(1, 5): |
| data = f"厨师{name} 生产出了第{i}道 {food}" |
| time.sleep(random.randint(1, 3)) |
| |
| queue_obj.put(data) |
| |
| queue_obj.put('q') |
| |
| |
| def customer(name, queue_obj): |
| while True: |
| from_producer_food = queue_obj.get() |
| if from_producer_food == 'q': |
| break |
| time.sleep(random.randint(1, 2)) |
| print(f"客人 {name} 吃了{from_producer_food}") |
| |
| |
| def main(): |
| task_list = [] |
| |
| queue_obj = Queue() |
| |
| producer_heart = Process(target=producer, args=('heart', '小炒肉', queue_obj)) |
| producer_god = Process(target=producer, args=('god', '酸菜鱼', queue_obj)) |
| |
| |
| customer_pp = Process(target=customer, args=('pp', queue_obj)) |
| customer_godfather = Process(target=customer, args=('godfather', queue_obj)) |
| |
| task_list.append(producer_heart) |
| task_list.append(producer_god) |
| task_list.append(customer_pp) |
| task_list.append(customer_godfather) |
| |
| |
| task_list_new = [] |
| for task in task_list: |
| task.start() |
| task_list_new.append(task) |
| for task in task_list_new: |
| task.join() |
| |
| |
| if __name__ == '__main__': |
| main() |
| |
| |
| |
| |
| |
| |
| |
| |
| |
(3)JoinableQueue
模块
- 每当向队列对象中存入数据的时候,队列对象内部会有一个计数器 +1
- 每当调用一次 task_done() 方法的时候,队列对象内部的计数器 -1
- join() 当计数器为 0 时,继续执行代码
- 很好的解决了进程结束的问题task_done()
| from multiprocessing import Process, Queue, JoinableQueue |
| import random |
| import time |
| |
| |
| def producer(name, food, queue_obj): |
| for i in range(1, 5): |
| data = f"厨师{name} 生产出了第{i}道 {food}" |
| time.sleep(random.randint(1, 3)) |
| |
| queue_obj.put(data) |
| |
| |
| def customer(name, queue_obj): |
| while True: |
| from_producer_food = queue_obj.get() |
| time.sleep(random.randint(1, 2)) |
| print(f"客人 {name} 吃了{from_producer_food}") |
| queue_obj.task_done() |
| |
| |
| def main(): |
| |
| queue_obj = JoinableQueue() |
| |
| producer_heart = Process(target=producer, args=('heart', '小炒肉', queue_obj)) |
| producer_god = Process(target=producer, args=('god', '酸菜鱼', queue_obj)) |
| |
| |
| customer_pp = Process(target=customer, args=('pp', queue_obj)) |
| customer_godfather = Process(target=customer, args=('godfather', queue_obj)) |
| |
| producer_heart.start() |
| producer_god.start() |
| |
| |
| customer_pp.daemon = True |
| customer_godfather.daemon = True |
| |
| customer_pp.start() |
| customer_godfather.start() |
| |
| producer_heart.join() |
| producer_god.join() |
| |
| queue_obj.join() |
| |
| |
| if __name__ == '__main__': |
| main() |
| |
| |
| |
| |
| |
| |
| |
| |
| |
【二】队列模块queue
(1)介绍
- 在Python中,
queue
模块提供了用于实现队列的类,是多线程和多进程编程中常用的工具之一。队列是一种先进先出的数据结构,即最先进入队列的元素最先被取出。
(2)用法
这段代码使用了Python的 queue
模块,创建了一个大小为5的队列。以下是对代码的逐行解释:
import queue
: 引入Python标准库中的 queue
模块。
q = queue.Queue(maxsize=5)
: 创建了一个最大容量为5的队列对象 q
。这是一个标准的先进先出队列,如果队列已满,put()
操作会阻塞,直到有空间为止。
- 先向队列中依次放入了5个元素,即1、2、3、4、5。由于队列的最大容量是5,这时队列已经满了。
q.put(6)
: 这一行是一个注释,表示对于队列已满的情况,如果尝试再放入一个元素(例如6),put()
操作会阻塞,直到队列中有空间为止。
- 然后依次从队列中取出了5个元素,并打印出来。由于队列是先进先出,因此输出顺序是1、2、3、4、5。
q.put(6)
: 将元素6放入队列。由于之前已经取出了5个元素,队列此时有一个空间,因此放入6不会阻塞。
print(q.get())
: 从队列中取出元素6,并打印出来。
这段代码主要演示了队列的基本操作,包括放入元素 (put()
) 和取出元素 (get()
)。队列的容量控制了可以放入的元素数量,当队列满时,put()
操作会阻塞,当队列空时,get()
操作也会阻塞。
| import queue |
| |
| q = queue.Queue(maxsize=5) |
| |
| q.put(1) |
| q.put(2) |
| q.put(3) |
| q.put(4) |
| q.put(5) |
| |
| |
| print(q.get()) |
| print(q.get()) |
| print(q.get()) |
| print(q.get()) |
| print(q.get()) |
| |
| q.put(6) |
| print(q.get()) |
(3)其他方法
(1)put()
-
put(item, block=True, timeout=None)
-
item
: 要放入队列的元素。
-
block
: 如果队列已满,是否阻塞等待队列有空间。默认为 True
,即阻塞等待。
-
timeout
: 如果设置了 block=True
,指定阻塞等待的最大时间(秒)。如果超过此时间仍然无法放入元素,则抛出 queue.Full
异常。
| import queue |
| |
| q = queue.Queue(maxsize=5) |
| |
| q.put(1) |
(2)get()
-
get(block=True, timeout=None)
-
block
: 如果队列为空,是否阻塞等待队列有元素。默认为 True
,即阻塞等待。
-
timeout
: 如果设置了 block=True
,指定阻塞等待的最大时间(秒)。如果超过此时间仍然无法取出元素,则抛出 queue.Empty
异常。
| import queue |
| |
| q = queue.Queue(maxsize=5) |
| |
| q.put(1) |
| print(q.get()) |
(3)get_nowait()
get_nowait()
方法是 Queue
类的一个方法,用于从队列中获取一个元素,但不等待。如果队列为空,它会立马引发 queue.Empty
异常。与 get()
方法不同,get_nowait()
不会阻塞等待队列中有元素可供获取。
| import queue |
| |
| q = queue.Queue(maxsize=3) |
| |
| q.put(1) |
| q.put(2) |
| q.put(6) |
| print(q.get_nowait()) |
| print(q.get()) |
| print(q.get()) |
| print(q.get_nowait()) |
| |
| |
(4)full()
full()
方法用于检查队列是否已满。如果队列的元素数量达到了设定的最大容量(通过 maxsize
参数设置),则 full()
方法返回 True
;否则返回 False
。
| import queue |
| |
| q = queue.Queue(maxsize=3) |
| |
| q.put(1) |
| q.put(2) |
| q.put(6) |
| print(q.full()) |
| print(q.get()) |
| print(q.get()) |
| print(q.get()) |
| print(q.full()) |
(5)empty()
empty()
方法用于检查队列是否为空。如果队列中没有元素,则 empty()
方法返回 True
;否则返回 False
。
| import queue |
| |
| q = queue.Queue(maxsize=3) |
| |
| q.put(1) |
| q.put(2) |
| q.put(6) |
| print(q.empty()) |
| print(q.get()) |
| print(q.get()) |
| print(q.get()) |
| print(q.empty()) |
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 无需6万激活码!GitHub神秘组织3小时极速复刻Manus,手把手教你使用OpenManus搭建本
· Manus爆火,是硬核还是营销?
· 终于写完轮子一部分:tcp代理 了,记录一下
· 别再用vector<bool>了!Google高级工程师:这可能是STL最大的设计失误
· 单元测试从入门到精通