Synkroniseringsprimitiver

Källkod: Lib/asyncio/locks.py


asyncios synkroniseringsprimitiver är utformade för att likna dem i modulen threading med två viktiga förbehåll:

  • asyncioprimitiver är inte trådsäkra, därför bör de inte användas för synkronisering av OS-trådar (använd threading för det);

  • metoderna för dessa synkroniseringsprimitiver accepterar inte argumentet timeout; använd funktionen asyncio.wait_for() för att utföra operationer med timeouts.

asyncio har följande grundläggande synkroniseringsprimitiver:


Lås

class asyncio.Lock

Implementerar ett mutex-lås för asyncio-uppgifter. Inte tråd-säker.

Ett asynciolås kan användas för att garantera exklusiv tillgång till en delad resurs.

Det föredragna sättet att använda ett Lock är en async with-sats:

lock = asyncio.Lock()

# ... later
async with lock:
    # access shared state

vilket är likvärdigt med:

lock = asyncio.Lock()

# ... later
await lock.acquire()
try:
    # access shared state
finally:
    lock.release()

Ändrad i version 3.10: Parametern loop har tagits bort.

async acquire()

Förvärva låset.

Den här metoden väntar tills låset är unlocked, sätter det till locked och returnerar True.

När mer än en coroutine blockeras i acquire() i väntan på att låset ska låsas upp, fortsätter bara en coroutine till slut.

Att förvärva ett lås är rättvist: den coroutine som fortsätter kommer att vara den första coroutine som började vänta på låset.

release()

Lossa låset.

När låset är locked, återställ det till unlocked och returnera.

Om låset är olåst uppstår ett RuntimeError.

locked()

Returnerar True om låset är locked.

Event

class asyncio.Event

Ett händelseobjekt. Inte tråd-säkert.

En asynciohändelse kan användas för att meddela flera asynciouppgifter att en händelse har inträffat.

Ett Event-objekt hanterar en intern flagga som kan sättas till true med metoden set() och återställas till false med metoden clear(). Metoden wait() blockerar tills flaggan är satt till true. Flaggan är initialt satt till false.

Ändrad i version 3.10: Parametern loop har tagits bort.

Exempel:

async def waiter(event):
    print('waiting for it ...')
    await event.wait()
    print('... got it!')

async def main():
    # Create an Event object.
    event = asyncio.Event()

    # Spawn a Task to wait until 'event' is set.
    waiter_task = asyncio.create_task(waiter(event))

    # Sleep for 1 second and set the event.
    await asyncio.sleep(1)
    event.set()

    # Wait until the waiter task is finished.
    await waiter_task

asyncio.run(main())
async wait()

Vänta tills händelsen är inställd.

Om händelsen är inställd, returnera True omedelbart. Annars blockeras tills en annan uppgift anropar set().

set()

Ställ in händelsen.

Alla uppgifter som väntar på att händelsen ska aktiveras kommer omedelbart att väckas.

clear()

Rensa (avmarkera) händelsen.

Efterföljande uppgifter som väntar på wait() kommer nu att blockeras tills metoden set() anropas igen.

is_set()

Returnerar True om händelsen är inställd.

Villkor

class asyncio.Condition(lock=None)

Ett Condition-objekt. Inte tråd-säkert.

En asyncio-villkorsprimitiv kan användas av en uppgift för att vänta på att en händelse ska inträffa och sedan få exklusiv tillgång till en delad resurs.

I huvudsak kombinerar ett Condition-objekt funktionaliteten hos en Event och en Lock. Det är möjligt att låta flera Condition-objekt dela ett Lock, vilket gör det möjligt att samordna exklusiv tillgång till en delad resurs mellan olika uppgifter som är intresserade av särskilda tillstånd för den delade resursen.

Det valfria lock-argumentet måste vara ett Lock-objekt eller None. I det senare fallet skapas ett nytt Lock-objekt automatiskt.

Ändrad i version 3.10: Parametern loop har tagits bort.

Det föredragna sättet att använda ett villkor är en async with-sats:

cond = asyncio.Condition()

# ... later
async with cond:
    await cond.wait()

vilket är likvärdigt med:

cond = asyncio.Condition()

# ... later
await cond.acquire()
try:
    await cond.wait()
finally:
    cond.release()
async acquire()

Förvärva det underliggande låset.

Denna metod väntar tills det underliggande låset är unlocked, sätter det till locked och returnerar True.

notify(n=1)

Väck n uppgifter (1 som standard) som väntar på detta villkor. Om färre än n uppgifter väntar väcks de alla.

Låset måste förvärvas innan denna metod anropas och släppas kort därefter. Om metoden anropas med ett olåst lås kommer ett RuntimeError-fel att uppstå.

locked()

Returnerar True om det underliggande låset är förvärvat.

notify_all()

Väck alla uppgifter som väntar på detta villkor.

Den här metoden fungerar som notify(), men väcker alla väntande uppgifter.

Låset måste förvärvas innan denna metod anropas och släppas kort därefter. Om metoden anropas med ett olåst lås kommer ett RuntimeError-fel att uppstå.

release()

Lossa det underliggande låset.

När den anropas på ett olåst lås uppstår ett RuntimeError.

async wait()

Vänta tills du får besked.

Om den anropande uppgiften inte har förvärvat låset när denna metod anropas, uppstår ett RuntimeError.

Den här metoden frigör det underliggande låset och blockerar sedan tills den väcks av ett anrop från notify() eller notify_all(). När den har väckts återtar Condition sitt lås och denna metod returnerar True.

Observera att en uppgift kan återvända från detta anrop på ett felaktigt sätt, vilket är anledningen till att anroparen alltid bör kontrollera tillståndet igen och vara beredd att wait() igen. Av denna anledning kanske du föredrar att använda wait_for() istället.

async wait_for(predicate)

Vänta tills ett predikat blir sant.

Predikatet måste vara en callable vars resultat kommer att tolkas som ett booleskt värde. Metoden kommer att upprepa wait() tills predikatet utvärderas till true. Det slutliga värdet är returvärdet.

Semafor

class asyncio.Semaphore(value=1)

Ett semaforobjekt. Inte tråd-säkert.

En semafor hanterar en intern räknare som decimeras vid varje anrop av acquire() och ökas vid varje anrop av release(). Räknaren kan aldrig gå under noll; när acquire() upptäcker att den är noll blockeras den och väntar tills någon uppgift anropar release().

Det valfria argumentet value anger det initiala värdet för den interna räknaren (1 som standard). Om det angivna värdet är mindre än 0 uppstår ett ValueError.

Ändrad i version 3.10: Parametern loop har tagits bort.

Det föredragna sättet att använda en semafor är en async with-sats:

sem = asyncio.Semaphore(10)

# ... later
async with sem:
    # work with shared resource

vilket är likvärdigt med:

sem = asyncio.Semaphore(10)

# ... later
await sem.acquire()
try:
    # work with shared resource
finally:
    sem.release()
async acquire()

Förvärva en semafor.

Om den interna räknaren är större än noll, decimera den med ett och returnera True omedelbart. Om den är noll, vänta tills en release() anropas och returnera True.

locked()

Returnerar True om semaforen inte kan förvärvas omedelbart.

release()

Frigör en semafor, vilket ökar den interna räknaren med ett steg. Kan väcka en uppgift som väntar på att förvärva semaforen.

Till skillnad från BoundedSemaphore tillåter Semaphore fler release() -anrop än acquire() -anrop.

BoundedSemaphore

class asyncio.BoundedSemaphore(value=1)

Ett avgränsat semaforobjekt. Inte tråd-säkert.

Bounded Semaphore är en version av Semaphore som ger upphov till ett ValueError i release() om den ökar den interna räknaren över det ursprungliga värdet.

Ändrad i version 3.10: Parametern loop har tagits bort.

Barrier

class asyncio.Barrier(parties)

Ett barriärobjekt. Inte tråd-säkert.

En barriär är en enkel synkroniseringsprimitiv som gör det möjligt att blockera tills parties antal uppgifter väntar på den. Uppgifter kan vänta på wait()-metoden och blockeras tills det angivna antalet uppgifter slutar vänta på wait(). Vid den tidpunkten kommer alla väntande uppgifter att avblockeras samtidigt.

async with kan användas som ett alternativ till att vänta på wait().

Barriären kan återanvändas ett obegränsat antal gånger.

Exempel:

async def example_barrier():
   # barrier with 3 parties
   b = asyncio.Barrier(3)

   # create 2 new waiting tasks
   asyncio.create_task(b.wait())
   asyncio.create_task(b.wait())

   await asyncio.sleep(0)
   print(b)

   # The third .wait() call passes the barrier
   await b.wait()
   print(b)
   print("barrier passed")

   await asyncio.sleep(0)
   print(b)

asyncio.run(example_barrier())

Resultatet av detta exempel är:

<asyncio.locks.Barrier object at 0x... [filling, waiters:2/3]>
<asyncio.locks.Barrier object at 0x... [draining, waiters:0/3]>
barrier passed
<asyncio.locks.Barrier object at 0x... [filling, waiters:0/3]>

Tillagd i version 3.11.

async wait()

Passera spärren. När alla uppgifter som är parter i barriären har anropat denna funktion, avblockeras alla samtidigt.

När en väntande eller blockerad uppgift i barriären avbryts, lämnar denna uppgift barriären som förblir i samma tillstånd. Om barriärens tillstånd är ”fyllning”, minskar antalet väntande uppgifter med 1.

Returvärdet är ett heltal i intervallet 0 till parties-1, olika för varje uppgift. Detta kan användas för att välja en uppgift för att göra några speciella hushållsarbeten, t.ex.:

...
async with barrier as position:
   if position == 0:
      # Only one task prints this
      print('End of *draining phase*')

Denna metod kan ge upphov till ett BrokenBarrierError undantag om barriären bryts eller återställs medan en uppgift väntar. Den kan ge upphov till ett CancelledError om en uppgift avbryts.

async reset()

Återställer barriären till standardtillståndet, tom. Alla uppgifter som väntar på den kommer att få undantaget BrokenBarrierError.

Om en barriär är bruten kan det vara bättre att bara lämna den och skapa en ny.

async abort()

Sätter barriären i ett trasigt tillstånd. Detta gör att alla aktiva eller framtida anrop till wait() misslyckas med BrokenBarrierError. Använd detta till exempel om en av uppgifterna måste avbrytas, för att undvika oändligt väntande uppgifter.

parties

Det antal uppgifter som krävs för att passera barriären.

n_waiting

Antalet uppgifter som för närvarande väntar i barriären medan den fylls på.

broken

En boolean som är True om barriären är i ett trasigt tillstånd.

exception asyncio.BrokenBarrierError

Detta undantag, en underklass till RuntimeError, uppstår när objektet Barrier återställs eller bryts.


Ändrad i version 3.9: Att förvärva ett lås med hjälp av await lock eller yield from lock och/eller with-satsen (with await lock, with (yield from lock)) togs bort. Använd async with lock istället.