更新:编辑标题以关注主要问题。请参阅我的答案以获取完整更新。
在下面的代码中,a()
和b()
是相同的。它们中的每一个同时从 0 计数到 9,同时每 2 个计数获取并产生一个锁。
import asyncio
lock = asyncio.Lock()
def a ():
yield from lock.acquire()
for i in range(10):
print('a: ' + str(i))
if i % 2 == 0:
lock.release()
yield from lock.acquire()
lock.release()
def b ():
yield from lock.acquire()
for i in range(10):
print('b: ' + str(i))
if i % 2 == 0:
lock.release()
yield from lock.acquire()
lock.release()
asyncio.get_event_loop().run_until_complete(asyncio.gather(a(), b()))
print('done')
我期望交错输出,但我得到:
b: 0
b: 1
b: 2
b: 3
b: 4
b: 5
b: 6
b: 7
b: 8
b: 9
a: 0
a: 1
a: 2
a: 3
a: 4
a: 5
a: 6
a: 7
a: 8
a: 9
done
似乎第二个yield
实际上并没有产生,而是立即重新获取锁并继续。
这对我来说似乎是一个错误。我对吗?还是有其他解释?
下面的代码,用额外的初始 "noop" 进行了修改yield
,可以正常工作。这让我相信锁确实是公平的并且可能是正确的。
import asyncio
lock = asyncio.Lock()
def a ():
yield from lock.acquire()
yield from asyncio.sleep(0)
for i in range(10):
print('a: ' + str(i))
if i % 2 == 0:
lock.release()
yield from lock.acquire()
lock.release()
def b ():
yield from lock.acquire()
yield from asyncio.sleep(0)
for i in range(10):
print('b: ' + str(i))
if i % 2 == 0:
lock.release()
yield from lock.acquire()
lock.release()
asyncio.get_event_loop().run_until_complete(asyncio.gather(a(), b()))
print('done')
输出:
a: 0
b: 0
a: 1
a: 2
b: 1
b: 2
a: 3
a: 4
b: 3
b: 4
a: 5
a: 6
b: 5
b: 6
a: 7
a: 8
b: 7
b: 8
a: 9
b: 9
done
请注意,我在一开始只做一次无操作产量,而不是每 2 次。然而,这样做会导致在第一段代码中按预期每 2 个计数进行交错。
调度程序中只有一些优化(我认为是一个错误),yield
当获取没有其他人正在等待的锁时,它并没有真正实现。
如何解释第一个输出?