Python asyncio.Lock() 有啥用?
Posted
技术标签:
【中文标题】Python asyncio.Lock() 有啥用?【英文标题】:what's Python asyncio.Lock() for?Python asyncio.Lock() 有什么用? 【发布时间】:2014-09-12 01:55:09 【问题描述】:是不是因为协程将来可能会被抢占?或者它允许人们在关键部分使用产量(不应该鼓励 IMO)?
【问题讨论】:
这是一个锁,不确定它与抢占有什么关系,当您尝试获取锁时,您会特别放弃控制权,希望在您持有锁的情况下将其还给您。这是为了让一个协程一次可以进入一个临界区。这一切都在文档中。 我认为您没有仔细阅读官方文档或理解问题。 【参考方案1】:您使用它的原因与在线程代码中使用锁的原因相同:保护关键部分。 asyncio
主要用于单线程代码,但仍会发生并发执行(任何时候遇到yield from
或await
),这意味着有时您需要同步。
例如,考虑一个从 Web 服务器获取一些数据,然后缓存结果的函数:
async def get_stuff(url):
if url in cache:
return cache[url]
stuff = await aiohttp.request('GET', url)
cache[url] = stuff
return stuff
现在假设您有多个协同程序同时运行,可能需要使用get_stuff
的返回值:
async def parse_stuff():
stuff = await get_stuff("www.example.com/data")
# do some parsing
async def use_stuff():
stuff = await get_stuff("www.example.com/data")
# use stuff to do something interesting
async def do_work():
out = await aiohttp.request("www.awebsite.com")
# do some work with out
loop = asyncio.get_event_loop()
loop.run_until_complete(asyncio.gather(
parse_stuff(),
use_stuff(),
do_work(),
))
现在,假设从url
获取数据是慢。如果parse_stuff
和use_stuff
同时运行,每个都将承担通过网络获取stuff
的全部成本。如果你用锁保护方法,你可以避免这种情况:
stuff_lock = asyncio.Lock()
async def get_stuff(url):
async with stuff_lock:
if url in cache:
return cache[url]
stuff = await aiohttp.request('GET', url)
cache[url] = stuff
return stuff
另外需要注意的是,当一个协程在get_stuff
中时,调用aiohttp
,另一个等待stuff_lock
,第三个协程根本不需要调用get_stuff
也可以运行,不受 Lock
上的协程阻塞的影响。
显然这个例子有点做作,但希望它能让您了解asyncio.Lock
为何有用;它允许您保护关键部分,而不会阻止其他协同程序运行,而 不需要 需要访问该关键部分。
【讨论】:
感谢您的详细解释。让我总结一下(对于不理解问题的人)。 1) 协程不能被抢占——它们一直运行直到“yield from”将控制权返回给循环。 2) asyncio.Lock() 用于保护调用“yield from”的关键部分——否则不需要使用锁。讨论假设单线程 asyncio 使用模式。 dano - 在这个特定的示例中(请记住,它确实是人为设计的),如果您只是删除yield from
并发出阻塞 HTTP 请求,您不会获得相同的性能吗?因为在锁定状态下,只有 1 个 get_stuff()
实例会一直在进行中。 (如,实例 B 只能在实例 A 完全完成时启动。)
@NickChammas 如果整个应用程序中运行的唯一协程正在等待get_stuff
,那么性能将是相当的。但是,如果您有其他协程运行其他不需要锁的方法,那么不;阻塞的 HTTP 请求会阻塞所有其他协程,完全停止您的应用程序,直到它完成。使用asyncio.Lock
允许所有其他协程继续运行。
@SamanthaAtkins 我的回答解释了为什么有时这仍然是必要的。基本上,因为您可以同时运行多个协同程序,您仍然需要同步以防止它们同时进入关键部分。在asyncio
的情况下,关键部分只需要使用yield from
或await
调用锁定是必要的。
@SamanthaAtkins 他们不能并行运行,但可以同时运行。我的回答再次给出了一个用例,其中没有锁定的并发执行会导致意外行为。【参考方案2】:
一个例子是当你只希望一些代码运行一次,却被很多人请求(例如在一个网络应用程序中)
async def request_by_many():
key = False
lock = asyncio.Lock()
async with lock:
if key is False:
await only_run_once()
async def only_run_once():
while True:
if random()>0.5:
key = True
break
await asyncio.sleep(1)
【讨论】:
以上是关于Python asyncio.Lock() 有啥用?的主要内容,如果未能解决你的问题,请参考以下文章