Skip to main content
 首页 » 编程设计

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

2024年11月01日41mayingbao

有什么方法可以创建一个可以在 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() ......但是因为线程 B 中只有一个 greenlet的 hub,引发“永远阻止”异常。

所以!有什么可以做的吗?还是我卡住了?

请您参考如下方法:

我不能确定这个代码片段是否被认为是池。但是请检查一下。

gevent 中的所有 demn 点是它是异步的。例如,如果您需要请求 100 个 html 页面(没有 gevent)。您向第一页发出第一个请求,并且您的 python 解释器被卡住,直到响应准备就绪。所以 gevent 允许卡住那些第一个请求的输出并移动到第二个,这意味着不要浪费时间。 所以我们可以很容易地在这里进行猴子补丁。但是如果需要将请求的结果写入数据库(比如couchdb,couchdb是有revisions的,也就是说文档要同步修改)。这里我们可以使用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) 

现在让我们发现输出文件。这是来自同步结果。

这是来自涉及 gevent 的脚本。

如您所见,当使用 gevent 时,响应没有按顺序出现。所以谁先响应先写入文件。主要部分让我们看看我们在使用gevent时节省了多少时间。

注意事项:在上面的示例中,我们不需要锁定写入(追加)到文件。但对于 couchdb,这是必需的。因此,当您将 Semaphore 与带有 get-save 文档的 couchdb 一起使用时,您不会遇到文档冲突!