8.8. Zárak¶
A asyncio.Lock kölcsönös kizárást biztosít a korutinok között – garantálja, hogy egyszerre csak egy korutin birtokolja a zárat, a többiek pedig várnak, amíg a birtokló elengedi azt.
8.8.1. Mikor van szükség zárra¶
A kooperatív konkurencia oldal megjegyezte, hogy két korutin nem fűződhet össze egy olyan kódszakasz közepén, amelyben nincs await. Ennek az ellenkezője is igaz: amint egy korutin await-ol, a ciklus szabadon futtathat egy másik korutint. Ha két korutin awaiteken keresztül ugyanazt az erőforrást érinti – egy UART, I2C vagy SPI buszt –, a műveleteik olyan módon fűződhetnek össze, ami megrongálja az erőforrást.
Egy zár a kritikus szakasz köré bezárja ezt a rést:
import asyncio
bus_lock = asyncio.Lock()
async def read_register(bus, addr):
async with bus_lock:
bus.write(addr)
return await bus.read(2)
Most már mindkét korutin egyidejűleg hívhatja a read_register függvényt; a zár gondoskodik arról, hogy egyszerre csak egyikük birtokolja a buszt, a másik pedig megvárja, amíg a zárat elengedik, mielőtt elindulna.
Zárakra nincs szükség, ha a kritikus szakaszon belül nincs await – a kooperatív ütemezés garanciája erre az esetre már gondoskodik. Csak akkor van rájuk szükség, ha a kritikus szakasz menet közben átadja a vezérlést a ciklusnak.
8.8.2. Az async with idióma¶
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.
Arra a ritka esetre, amikor a zár élettartama nem esik egybe egy kódblokkal, a metódusok közvetlenül is elérhetők:
await bus_lock.acquire()
try:
bus.write(addr)
result = await bus.read(2)
finally:
bus_lock.release()
A try/finally szükséges ahhoz, hogy ez egyenértékű legyen az async with verzióval. Az async with forma azért létezik, mert ez a helyes alak, és a nyelv tömörré teszi.
8.8.3. Metódus-referencia¶
acquire()– egy korutin. Addig blokkol, amíg a zár fel nem oldódik, majd megszerzi azt.release()– elengedi a zárat. Ha bármely korutin aacquire()hívásra várakozva sorban áll, a sorban következő ütemezésre kerül, és a zár zárva marad; egyébként a zár feloldódik.locked()–Trueértéket ad vissza, ha a zárat jelenleg birtokolják, egyébkéntFalseértéket. Azonnal visszatér; nem blokkol.
A várakozók FIFO sorrendben kerülnek kiszolgálásra. Nincs prioritás, nincs újrabelépés (ugyanaz a feladat nem szerezhet meg egy olyan zárat, amelyet már birtokol), és nincs időtúllépés a megszerzésnél. Ahhoz, hogy határidőt szabjunk egy zár megszerzésére, csomagoljuk a megszerzést egy asyncio.wait_for() hívásba:
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()