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

具体来说,我的应用程序主要基于 gevent,但有些部分需要在“真实”线程中运行……但这会导致 logging 处理程序出现问题,因为它们在某些操作周围使用信号量,产生类似于以下内容的异常:

文件“/System/Library/Frameworks/Python.framework/Versions/2.7/lib/python2.7/logging/__init__.py”,第 1300 行,在 callHandlers 中
hdlr.handle(记录)
文件“/System/Library/Frameworks/Python.framework/Versions/2.7/lib/python2.7/logging/__init__.py”,第 742 行,在句柄中
self.acquire()
文件“/System/Library/Frameworks/Python.framework/Versions/2.7/lib/python2.7/logging/__init__.py”,第 693 行,在获取中
self.lock.acquire()
文件“/System/Library/Frameworks/Python.framework/Versions/2.7/lib/python2.7/threading.py”,第 128 行,在获取中
rc = self.__block.acquire(阻塞)
文件“_semaphore.pyx”,第 112 行,在 gevent._semaphore.Semaphore.acquire (gevent/gevent._semaphore.c:2984)
文件“.../gevent/hub.py”,第 331 行,在 switch 中
返回 greenlet.switch(self)
LoopExit:此操作将永远阻塞

我怀疑,当线程 A 持有锁时,会发生这种情况,然后线程 B 尝试获取它。在注意到锁已经被持有时,线程 B 尝试 hub.switch() ……但由于线程 B 的 hub 中只有一个 greenlet,因此引发了“永远阻塞”异常。

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

最佳答案

我无法确定此代码片段是否被视为池。但是看看这个。

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)

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

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

这是来自涉及 gevent 的脚本。
python - 在 ThreadPool 线程之间共享 gevent 锁/信号量?-LMLPHP

正如您所看到的,当使用 gevent 时,响应的顺序不正确。所以谁的响应先写在文件中。主要部分让我们看看使用 gevent 时我们节省了多少时间。

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

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

关于python - 在 ThreadPool 线程之间共享 gevent 锁/信号量?,我们在Stack Overflow上找到一个类似的问题:https://stackoverflow.com/questions/22108576/

10-13 04:07