![](/img/trans.png)
[英]Python and gevent - AttributeError: 'module' object has no attribute 'queue'
[英]Python Gevent Shared Queue (Listener Process)
我正在嘗試一些代碼,在這里我可以使用gevent實現登錄到多線程程序。 我想做的是設置自定義日志記錄處理程序,以將日志事件放入隊列,而偵聽器進程一直在監視要適當處理的新日志事件。 我過去使用Multiprocessing做到了這一點,但從未使用Gevent做到過。
我遇到一個問題,該程序陷入無限循環(偵聽器進程),並且不允許其他線程“正常工作” ...
理想情況下,在工作進程完成后,我可以將一個任意值傳遞給偵聽器進程,以使其中斷循環,然后將所有進程連接在一起。 這是我到目前為止的內容:
import gevent
from gevent.pool import Pool
import Queue
import random
import time
def listener(q):
while True:
if not q.empty():
num = q.get()
print "The number is: %s" % num
if num <= 100:
print q.get()
# got passed 101, break out
else:
break
else:
continue
def worker(pid,q):
if pid == 0:
listener(q)
else:
gevent.sleep(random.randint(0,2)*0.001)
num = random.randint(1,100)
q.put(num)
def main():
q = Queue.Queue()
all_threads = []
all_threads = [gevent.spawn(worker, pid,q) for pid in xrange(10)]
gevent.wait(all_threads[1:])
q.put(101)
gevent.joinall(all_threads)
if __name__ == '__main__':
main()
正如我說的那樣,該程序似乎掛在了第一個程序上,並且不允許其他工人做他們的事情。 我還嘗試過完全單獨地單獨生成偵聽器進程(實際上是我寧願這樣做的方式),但是這似乎也沒有用,所以我嘗試了這種方式。
任何幫助將不勝感激,感覺我可能只是缺少有關gevent后端的明顯信息。
謝謝
第一個問題是,如果隊列最初為空,則您的監聽器永遠不會屈服。 您產生的第一個任務是您的聽眾。 當它開始時,有一段while True:
,q將為空,因此您轉到else分支,該分支將繼續,循環回到while循環的開始,然后q仍然為空。 因此,您只是坐在第一個線程中,不斷檢查q是否為空。
這里的關鍵是gevent不使用“本機”線程或進程。 與“真實”線程不同,“真實”線程可以隨時通過幕后操作(例如OS調度程序)切換到該線程,而gevent使用“ greenlets”,這要求您執行一些操作才能“屈服於”另一項任務。 gevent認為會阻塞的東西,例如從網絡,磁盤讀取或使用阻塞的gevent操作之一。
一種粗略的解決方法是在pid == 9
而不是0時啟動您的偵聽器。最后一次生成它,則q中將有項目,並且它將進入主if分支。 缺點是這不能解決邏輯問題,因此第一次隊列為空時,您將再次陷入無限循環。
一個更正確的解決方法是放入gevent.sleep()
而不是continue
。 睡眠是一項阻塞性操作,因此您的其他任務將有機會運行。 如果沒有參數,它不會等待任何時間,但是如果准備好運行,它仍然使gevent有機會決定切換到另一個任務。 但是,這仍然不是很有效,就好像Queue是空的一樣,它將花費大量的時間反復檢查,並要求盡快重新運行。 睡眠時間長於默認值0會更有效,但會延遲處理日志消息。
但是,您可以利用以下事實:許多gevent的類型(例如Queue)可以以更多的Python方式使用,並使您的代碼更簡單,更易於理解以及更高效。
import gevent
from gevent.queue import Queue
def listener(q):
for msg in q:
print "the number is %d" % msg
def worker(pid,q):
gevent.sleep(random.randint(0,2)*0.001)
num = random.randint(1,100)
q.put(num)
def main():
q = Queue()
listener_task = gevent.spawn(listener, q)
worker_tasks = [gevent.spawn(worker, pid, q) for pid in xrange(1, 10)]
gevent.wait(worker_tasks)
q.put(StopIteration)
gevent.join(listener_task)
在這里, Queue
可以在for
循環中充當迭代器。 只要有消息,它將獲得一個項目,運行循環,然后等待另一個項目。 如果沒有任何物品,它只會阻塞並徘徊,直到下一個物品到達為止。 由於gevent會阻塞,因此它將切換到您要運行的其他任務之一,從而避免了示例代碼遇到的無限循環問題。
因為此版本將Queue
用作for循環迭代器,所以還會自動提供一個不錯的哨兵值,我們可以將其放入隊列中以退出偵聽器任務。 如果for循環從其迭代器獲取StopIteration
,它將干凈地退出。 因此,當從q讀取的for循環從q獲取StopIteration
,它將退出,然后函數退出,並完成了產生的任務。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.