在ThreadPool线程之间共享gevent锁/信号量?

时间:2014-03-01 00:48:36

标签: python gevent

有没有办法制作一个可以在greenlets和ThreadPool线程之间共享的锁?

具体来说,我的应用程序主要是基于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

这种情况正在发生,我怀疑,当线程 A 持有锁时,线程 B 会尝试获取它。在注意到锁已被保持时,线程 B 尝试hub.switch() ...但由于线程中只有一个greenlet B hub ,提出了“永远阻止”的例外。

原来如此!有什么可以做的吗?还是我被卡住了?

1 个答案:

答案 0 :(得分:2)

我无法确定此代码snipplet是否被视为池。但请检查一下。

gevent中的所有demn点都是异步的。例如,如果您需要请求100个html页面(没有gevent)。您首先向第一页请求,并且您的python解释器将被冻结,直到响应准备就绪。因此gevent允许冻结那些第一个请求的输出并移动到第二个,这意味着不要浪费时间。 所以我们可以轻松地在这里修补补丁。但是如果你需要将请求的结果写入数据库(例如couchdb,则couchdb有修订版,这意味着文档应该同步修改)。在这里我们可以使用Semaphore。

让我们做一些该死的代码(这是同步的例子):

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,这是必需的。因此,当您使用带有获取保存文档的couchdb的Semaphore时,您不会获得文档冲突!