8.8. Zámky¶
asyncio.Lock poskytuje vzájemné vyloučení mezi korutinami – záruku, že zámek drží vždy pouze jedna korutina v daném okamžiku, zatímco ostatní čekají, dokud jej držitel neuvolní.
8.8.1. Kdy je zámek potřeba¶
Stránka kooperativní souběžnost uváděla, že dvě korutiny se nemohou prokládat uprostřed kódu, který neobsahuje žádné await. Platí to i naopak: jakmile korutina zavolá await, smyčka může spustit jinou korutinu. Pokud dvě korutiny pracují se stejným prostředkem napříč await body – sběrnicí UART, I2C nebo SPI – jejich operace se mohou proložit způsobem, který prostředek poškodí.
Zámek kolem kritické sekce tuto mezeru uzavírá:
import asyncio
bus_lock = asyncio.Lock()
async def read_register(bus, addr):
async with bus_lock:
bus.write(addr)
return await bus.read(2)
Dvě korutiny nyní mohou obě souběžně volat read_register; zámek zajišťuje, že sběrnici drží vždy jen jedna z nich, a druhá čeká na uvolnění zámku, než začne.
Zámky nejsou potřeba, když kritická sekce neobsahuje uvnitř žádné await – na tento případ se již vztahuje záruka kooperativního plánování. Jsou potřeba pouze tehdy, když kritická sekce uprostřed předá řízení smyčce.
8.8.2. Idiom async with¶
The example above shows the recommended way to use a lock:
inside an async with block. On entry the block awaits acquire(); on exit (whether the block
returned normally, raised an exception, or was cancelled)
the lock is released automatically. There is no path out of
the block that leaves the lock held.
Pro vzácný případ, kdy se životnost zámku neshoduje s blokem kódu, jsou metody dostupné i přímo:
await bus_lock.acquire()
try:
bus.write(addr)
result = await bus.read(2)
finally:
bus_lock.release()
Konstrukce try/finally je nutná, aby byla ekvivalentní verzi async with. Forma async with existuje právě proto, že toto je správný tvar a jazyk jej činí stručným.
8.8.3. Přehled metod¶
acquire()– korutina. Blokuje, dokud není zámek odemčen, a poté jej získá.release()– uvolní zámek. Pokud jsou nějaké korutiny ve frontě čekající naacquire(), je naplánováno spuštění další z fronty a zámek zůstává uzamčen; jinak se zámek odemkne.locked()– vracíTrue, pokud je zámek aktuálně držen, jinakFalse. Vrací se okamžitě; neblokuje.
Čekající jsou obsluhováni v pořadí FIFO. Neexistuje žádná priorita, žádná reentrance (stejná úloha nemůže získat zámek, který už drží) ani timeout při získávání. Chcete-li nastavit pro získání zámku časový limit, obalte získání do asyncio.wait_for()
try:
await asyncio.wait_for(bus_lock.acquire(), timeout=1)
except asyncio.TimeoutError:
# lock busy for >1 s -- bail out
return None
try:
...
finally:
bus_lock.release()