簡體   English   中英

Python Gevent共享隊列(偵聽器進程)

[英]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.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM