更新:編輯標題以關注主要問題。查看我的答案以獲得完整更新。asyncio中的鎖定優化失敗
在下面的代碼中,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個字。然而,這樣做會導致在第一段代碼中按照預期每隔兩次進行交錯。
在調度程序中只有一些優化(我認爲是一個錯誤),當獲取一個沒有其他人正在等待的鎖時,並不真正yield
。
如何解釋第一個輸出?
我相信這是asyncio鎖定實現中的一個錯誤。 https://github.com/python/cpython/blob/master/Lib/asyncio/locks.py#L171 – user2297550
什麼是你想實現:應該有一個'這行之前yield'? – Udi
@Udi我只是在學習asyncio,這看起來像是一個實現問題。我的GitHub的問題可能更清楚一點:https://github.com/python/asyncio/issues/486 – user2297550