进程之joinableQueue

joinableQueue是Python中一种特殊的队列,它提供了join()函数,用于阻塞直到队列中的所有数据被处理完毕。get()操作仅取出数据,而task_done()通知队列数据已被处理。调用task_done()的次数必须大于等于队列数据个数,join()才会结束阻塞。在不确定数据量的场景,如爬虫抓取未知数量页面时,joinableQueue能让主进程获知数据处理状态,从而结束子进程。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

joinableQueue

可翻译:为可join的队列

该队列相比普通的Queue的区别在于该对列额外增加的了join函数

 

join函数的作用:

该函数为阻塞函数,会阻塞直到等待队列中所有数据都被处理完毕。

q = JoinableQueue()
q.put(1) 
q.get()
q.join() #阻塞 等待队列中所有数据都被处理完毕
print("over")

执行以上函数,将导致进程无法结束,注释掉join调用就正常,发现join的确有阻塞的效果,

但是队列中一共就一个数据,明明已经调用get取出了,为什么join依然阻塞呢?

这是因为get仅仅是取出数据,而join是等待数据处理完毕,也就是说:

取出数据还不算完,你处理完以后必须告知队列处理完毕,通过task_done

q = JoinableQueue()
q.put(1) 
​
q.get()
q.task_done() # 数据处理完毕
​
q.join() #阻塞 等待队列中所有数据都被处理完毕
print("over")
#输出:
#   over

需要注意的时,task_done的调用次数必大于等于队列中的数据个数,join才能正常结束阻塞

q = JoinableQueue()
q.put(1) 
q.put(1) 
​
q.get()
q.task_done() # 数据处理完毕
​
​
q.join() #阻塞 等待队列中所有数据都被处理完毕
print("over")
#输出:
#   over

总结:

主进程可以明确知道队列中的数据何时被处理完毕

 

守护进程与joinablequeue的应用

回顾之前的生产者消费者模型中,生产者与消费者都明确要处理的数据数量,但是实际开发中很多情况是无法提前明确的,例如:要爬去一个网站上的所有页面,页面数量数不固定的

from multiprocessing import Process,JoinableQueue,Queue
import  time,random
def producter(name,q):
    for i in range(5):
        time.sleep(random.randint(1,2))
        print("\033[46m%s生产了 热狗%s\033[0m" % (name,i))
        q.put("%s的 热狗%s" % (name,i))
​
​
def customer(name,q):
    while True:
        time.sleep(random.randint(1, 2))
        hot_dog = q.get()
        print("\033[47m%s 吃掉了 %s \033[0m" % (name,hot_dog))
​
if __name__ == '__main__':
​
    q = Queue()
​
    p1 = Process(target=producter,args=("北京?店",q))
    p2 = Process(target=producter,args=("上海?店",q))
    p3 = Process(target=producter, args=("深圳?店", q))
    p1.start()
    p2.start()
    p3.start()
​
    c1 = Process(target=customer,args=("王思聪",q))
    c1.start()

上述代码无法正常运行结束,是因为消费者进程中不清楚处理是否处理完成,所以一直在循环等待数据。

此时我们就可以使用joinablequeue队列来让主进程获取生成者进程是否生成完毕的信号从而结束子进程

from multiprocessing import Process,JoinableQueue,Queue
​
​
​
​
# q = JoinableQueue()
#
# q.put(1)
# q.put(1)
#
# q.get()
# q.task_done()
#
#
# q.join() #阻塞 等待队列中所有数据都被处理完毕
# print("over")
​
import  time,random
def producter(name,q):
    for i in range(5):
        time.sleep(random.randint(1,3))
        print("\033[46m%s生产了 热狗%s\033[0m" % (name,i))
        q.put("%s的 热狗%s" % (name,i))
​
​
def customer(name,q):
    while True:
        time.sleep(random.randint(1, 2))
        hot_dog = q.get()
        print("\033[47m%s 吃掉了 %s \033[0m" % (name,hot_dog))
        # 一个数据处理完毕
        q.task_done()
​
if __name__ == '__main__':
​
    # q = Queue()
    q = JoinableQueue()
​
    p1 = Process(target=producter,args=("北京?店",q))
    p2 = Process(target=producter,args=("上海?店",q))
    p3 = Process(target=producter, args=("深圳?店", q))
    p1.start()
    p2.start()
    p3.start()
​
    c1 = Process(target=customer,args=("王思聪",q))
    c1.daemon = True # 使子进程跟随主进程结束
    c1.start()
​
    # 等待生产者进程全部生成完毕
    p1.join()
    p2.join()
    p3.join()
​
    # 等待所有数据全部处理完毕
    q.join()
​
    # 终止子进程 也可以开启子进程前将子进程设置为守护进程来结束子进程
    # c1.terminate()

进程池:

进程池与线程池使用方法完全一致,放到线程池一起讲

 

 

### Python 进程管理教程及最佳实践 #### 1. 使用 `multiprocessing` 模块创建和管理进程 Python 的 `multiprocessing` 模块提供了用于启动新进程的方法,这些方法类似于线程模块中的接口。然而,与线程不同的是,子进程中运行的函数将在独立的操作系统进程中执行。 ```python from multiprocessing import Process, current_process def worker(num): """简单的工作者函数""" print(f'Worker: {num}, PID: {current_process().pid}') if __name__ == '__main__': processes = [] for i in range(5): # 创建多个进程 p = Process(target=worker, args=(i,)) processes.append(p) p.start() for p in processes: p.join() # 等待所有子进程结束 ``` 这段代码展示了如何利用 `Process` 类来创建新的工作进程,并传递参数给它们[^1]。 #### 2. 实现进程间的同步机制 当涉及到共享资源时,确保各进程能够安全访问至关重要。为此,可以采用锁(Lock)、事件(Event)等同步原语来进行控制。 ```python from multiprocessing import Lock, Value lock = Lock() counter = Value('i', 0) def update_counter(): global counter with lock: counter.value += 1 print(f'Counter updated to {counter.value}') ``` 这里定义了一个全局计数器变量以及一个锁定对象,以防止竞争条件的发生。 #### 3. 利用队列进行跨进程通信 为了使不同的进程之间交换数据,推荐使用 `multiprocessing.Queue` 或者更高效的替代品如 `JoinableQueue` 来实现消息传递功能。 ```python from multiprocessing import Queue q = Queue() def producer(q): q.put([42, None, 'hello']) def consumer(q): data = q.get() print(data) p_producer = Process(target=producer, args=(q,)) p_consumer = Process(target=consumer, args=(q,)) p_producer.start(); p_consumer.start() p_producer.join(); p_consumer.join() ``` 此示例说明了生产者-消费者模式下两个进程间的数据传输过程[^2]。 #### 4. 日志记录优化建议 对于大型分布式应用来说,良好的日志策略不可或缺。考虑到多进程环境下的特殊需求,应该采取集中式的日志收集方案或是基于文件系统的轮转日志配置等方式提高效率并便于后续分析。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值