JoinableQueue 用于Python多进程生产者消费者问题的注意事项

参考文献:

http://lx.shellcodes.org/show.php?id=4

http://bofang.iteye.com/blog/1684345

JoinableQueue 比Queue多了task_done() 与join()两个函数,都是专用于全球进行编程的,当然多是生产者消费者问题。

task_done() 是用在get()后,告诉os, 我get完了,join()是说Queue里所有的items都被拿出来搞完了。

-----

Queue是Python标准库的队列实现。在网上经常看到示例代码是这样实现的: 

url = queue.get()
queue.task_done()
...

思路本身没什么问题——从队列中取一个数据,然后通知队列取成功了。但有些人代码是这样写的: 

url = queue.get()
queue.task_done()


try:
    sock = urllib2.urlopen(url)
except:
...

如果是在多线程环境下,这会带来个问题:若是url = queue.get()刚好把队列取空了,而由于执行了urlopen,所以当前线程产生了阻塞,发生了线程切换,如果执行权限刚好落在了主线程,这时如果主线程的queue.join将导致整个程序结束掉。而sock = urllib2.urlopen(url)后面的代码都还没来得及执行。 

为什么呢? 

我们看下Queue.py里对join函数如何实现的: 

79         self.all_tasks_done.acquire()
80         try:
81             while self.unfinished_tasks:    # unfinished_tasks保存了当前队列中的数目
82                 self.all_tasks_done.wait()
83         finally:
84             self.all_tasks_done.release()

上面代码里有个while循环,unfinished_tasks存储了当前队列里的数据数目,如果unfinished_tasks不为0,将会一直调用all_tasks_done.wait()阻塞住。只要unfinished_tasks不是0,join就可以一直工作下去。 

而task_done()正是减小了unfinished_tasks。不妨再看看task_done()的实现代码: 

59         self.all_tasks_done.acquire()
60         try:
61             unfinished = self.unfinished_tasks - 1
62             if unfinished <= 0:
63                 if unfinished < 0:
64                     raise ValueError('task_done() called too many times')
65                 self.all_tasks_done.notify_all()
66             self.unfinished_tasks = unfinished
67         finally:
68             self.all_tasks_done.release()

注意61行,表明每次执行了task_done(),unfinished_tasks就减1,直到unfinished_tasks为0。 

所以,在多线程下,想让队列变得正常的话,注意task_done的位置: 

url = queue.get()
...
try:
    sock = urllib2.urlopen(url)
except:
...
queue.task_done()   # 要在一切都搞定后,再告诉队列空了。

-----------
另外,还有一点要注意:
Queue是多进程安全的队列,可以使用Queue实现多进程之间的数据传递。put方法用以插入数据到队列中,put方法还有两个可选参数:blocked和timeout。如果blocked为True(默认值),并且timeout为正值,该方法会阻塞timeout指定的时间,直到该队列有剩余的空间。如果超时,会抛出Queue.Full异常。如果blocked为False,但该Queue已满,会立即抛出Queue.Full异常。

get方法可以从队列读取并且删除一个元素。同样,get方法有两个可选参数:blocked和timeout。如果blocked为True(默认值),并且timeout为正值,那么在等待时间内没有取到任何元素,会抛出Queue.Empty异常。如果blocked为False,有两种情况存在,如果Queue有一个值可用,则立即返回该值,否则,如果队列为空,则立即抛出Queue.Empty异常。




  • 2
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
生产者消费者问题是一个经典的多线程问题,但是在Python中,我们也可以使用多进程来解决这个问题。 下面是一个使用Python多进程处理生产者消费者问题的示例代码: ```python import multiprocessing import time # 生产者 def producer(queue): for i in range(5): print('Producing', i) queue.put(i) time.sleep(1) # 消费者 def consumer(queue): while True: item = queue.get() if item is None: break print('Consuming', item) time.sleep(2) if __name__ == '__main__': queue = multiprocessing.Queue() producer_process = multiprocessing.Process(target=producer, args=(queue,)) consumer_process = multiprocessing.Process(target=consumer, args=(queue,)) producer_process.start() consumer_process.start() producer_process.join() queue.put(None) consumer_process.join() ``` 在这个示例代码中,我们使用Python的`multiprocessing`模块来创建了两个进程,一个是生产者进程,一个是消费者进程。我们使用了一个`multiprocessing.Queue`对象来作为生产者和消费者之间的通信管道。生产者进程会不断地向这个管道中添加数据,而消费者进程则会不断地从管道中取出数据进行处理。 运行这个代码,你会看到生产者进程不断地输出生产的数据,而消费者进程则不断地输出消费的数据。当生产者进程生产完所有数据后,我们向管道中添加了一个`None`对象,以此来通知消费者进程数据已经处理完毕,可以退出了。最后,我们使用`join()`方法来等待两个进程都退出。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值