2014-03-01 68 views
2

有沒有什麼辦法可以在greenlet和ThreadPool線程之間共享一個鎖?在ThreadPool線程之間共享gevent鎖/信號量?

具體來說,我的應用程序主要是GEVENT爲主,但有些部分需要在「真實」的線程中運行......但是這出現問題的原因logging處理程序,因爲他們使用一個信號燈周圍的一些操作,產生類似的例外:

 
    File "/System/Library/Frameworks/Python.framework/Versions/2.7/lib/python2.7/logging/__init__.py", line 1300, in callHandlers 
    hdlr.handle(record) 
    File "/System/Library/Frameworks/Python.framework/Versions/2.7/lib/python2.7/logging/__init__.py", line 742, in handle 
    self.acquire() 
    File "/System/Library/Frameworks/Python.framework/Versions/2.7/lib/python2.7/logging/__init__.py", line 693, in acquire 
    self.lock.acquire() 
    File "/System/Library/Frameworks/Python.framework/Versions/2.7/lib/python2.7/threading.py", line 128, in acquire 
    rc = self.__block.acquire(blocking) 
    File "_semaphore.pyx", line 112, in gevent._semaphore.Semaphore.acquire (gevent/gevent._semaphore.c:2984) 
    File "…/gevent/hub.py", line 331, in switch 
    return greenlet.switch(self) 
LoopExit: This operation would block forever 

這正在發生,我懷疑,當線程一個持有鎖,那麼線程試圖獲取它。在注意到鎖已被佔用時,線程B嘗試hub.switch() ...但由於在線程Bhub中只有一個綠色小程序,因此引發了「永遠阻塞」異常。

所以!對此有什麼可以做的嗎?或者我卡住了?

+0

我遇到了一個非常類似的問題,https://github.com/numba/numba/issues/1556。真的很高興看到這個解決方案。 – Michael

回答

0

我不能確定這個代碼snipplet是否被認爲是池。但檢查一下。

gevent中的所有表述都是異步的。例如,如果你需要請求100個HTML頁面(沒有gevent)。你首先請求第一頁,並且你的python解釋器被凍結,直到響應準備就緒。所以gevent可以凍結那些第一個請求的輸出並移動到第二個,這意味着不要浪費時間。 因此,我們可以在這裏輕鬆地將猴子補丁全部打開。但是,如果您需要將請求的結果寫入數據庫(例如,couchdb,couchdb有修訂版,這意味着文檔應該同步修改)。在這裏,我們可以使用信號量。

讓我們做一些該死的代碼(在這裏是同步的例子):

import os 
import requests 
import time 

start = time.time() 
path = os.path.dirname(os.path.abspath(__file__)) 

test_sites = [ 
    'https://vimeo.com/', 
    'https://stackoverflow.com/questions/22108576/share-gevent-locks-semaphores-between-threadpool-threads', 
    'http://www.gevent.org/gevent.monkey.html#gevent.monkey.patch_all', 
    'https://www.facebook.com/', 
    'https://twitter.com/', 
    'https://www.youtube.com/', 
    'https://zaxid.net/', 
    'https://24tv.ua/', 
    'https://zik.ua/', 
    'https://github.com/' 
] 

# request each site and write request status into file 
def process_each_page(html_page): 
    # all requests are executed synchronously 
    response = requests.get(html_page) 
    with open(path + '/results_no_sema.txt', 'a') as results_file: 
     results_file.write(str(response.status_code) + ' ' +html_page +'\n') 

for page in test_sites: 
    process_each_page(page) 

print(time.time() - start) 

這裏是模擬代碼被捲入GEVENT:

from gevent import monkey 
monkey.patch_all() 

import gevent 
import os 
import requests 

from gevent.lock import Semaphore 
import time 

start = time.time() 

path = os.path.dirname(os.path.abspath(__file__)) 
gevent_lock = Semaphore() 

test_sites = [ 
    'https://vimeo.com/', 
    'https://stackoverflow.com/questions/22108576/share-gevent-locks-semaphores-between-threadpool-threads', 
    'http://www.gevent.org/gevent.monkey.html#gevent.monkey.patch_all', 
    'https://www.facebook.com/', 
    'https://twitter.com/', 
    'https://www.youtube.com/', 
    'https://zaxid.net/', 
    'https://24tv.ua/', 
    'https://zik.ua/', 
    'https://github.com/' 
] 

# request each site and write request status into file 
def process_each_page(html_page): 
    # here we dont need lock 
    response = requests.get(html_page) 

    gevent_lock.acquire() 
    with open(path + '/results.txt', 'a') as results_file: 
     results_file.write(str(response.status_code) + ' ' +html_page +'\n') 
    gevent_lock.release() 


gevent_greenlets = [gevent.spawn(process_each_page, page) for page in test_sites] 
gevent.joinall(gevent_greenlets) 
print(time.time() - start) 

現在,讓我們發現輸出文件。這是從同步結果。

enter image description here

這是從腳本,GEVENT參與。 enter image description here

正如你所看到的,當使用gevent時,響應不是按順序進行的。所以誰的迴應是第一個寫在文件中。主要部分讓我們看看使用gevent時我們保存了多少時間。

enter image description here

NOTA-BENE:例如,在上面我們不需要鎖定寫入(追加)到文件。但對於couchdb這是必需的。所以當你用帶有get-save文檔的couchdb使用Semaphore時,你不會得到衝突!