Python

동기화 프리미티브

소스 코드: Lib/asyncio/locks.py


asyncio 동기화 프리미티브는 threading 모듈의 것과 유사하도록 설계되었습니다만 두 가지 중요한 주의 사항이 있습니다:

  • asyncio 프리미티브는 스레드 안전하지 않으므로, OS 스레드 동기화(이를 위해서는 threading을 사용하십시오)에 사용하면 안 됩니다.

  • 이러한 동기화 프리미티브의 메서드는 timeout 인자를 받아들이지 않습니다; asyncio.wait_for() 함수를 사용하여 시간제한이 있는 연산을 수행하십시오.

asyncio에는 다음과 같은 기본 동기화 프리미티브가 있습니다:


Lock

class asyncio.Lock

asyncio 태스크를 위한 뮤텍스 록을 구현합니다. 스레드 안전하지 않습니다.

asyncio 록은 공유 자원에 대한 독점 액세스를 보장하는 데 사용될 수 있습니다.

Lock을 사용하는 가장 좋은 방법은 async with 문입니다:

lock = asyncio.Lock()

# ... 나중에
async with lock:
    # 공유 상태를 액세스합니다

이는 다음과 동등합니다:

lock = asyncio.Lock()

# ... 나중에
await lock.acquire()
try:
    # 공유 상태를 액세스합니다
finally:
    lock.release()

버전 3.10에서 변경: loop 매개 변수를 제거했습니다.

async acquire()

록을 얻습니다.

이 메서드는 록이 풀림(unlocked)이 될 때까지 기다리고, 잠김(locked)으로 설정한 다음 True를 반환합니다.

잠금이 해제되기를 기다리는 acquire()에서 둘 이상의 코루틴 블록 될 때, 결국 한 개의 코루틴만 진행됩니다.

록을 얻는 것은 공평(fair)합니다: 진행할 코루틴은 록을 기다리기 시작한 첫 번째 코루틴이 됩니다.

release()

록을 반납합니다.

록이 잠김(locked)이면 풀림(unlocked)으로 재설정하고 돌아옵니다.

록이 풀림(unlocked)이면 RuntimeError가 발생합니다.

locked()

록이 잠김(locked)이면 True를 반환합니다.

Event

class asyncio.Event

이벤트 객체. 스레드 안전하지 않습니다.

asyncio 이벤트는 어떤 이벤트가 발생했음을 여러 asyncio 태스크에 알리는 데 사용할 수 있습니다.

Event 객체는 set() 메서드로 으로 설정하고 clear() 메서드로 거짓으로 재설정할 수 있는 내부 플래그를 관리합니다. wait() 메서드는 플래그가 으로 설정될 때까지 블록합니다. 플래그는 초기에 거짓으로 설정됩니다.

버전 3.10에서 변경: loop 매개 변수를 제거했습니다.

예:

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

async def main():
    # Event 객체를 만듭니다.
    event = asyncio.Event()

    # 'event'가 설정될 때까지 대기 할 Task를 만듭니다.
    waiter_task = asyncio.create_task(waiter(event))

    # 1초 동안 잠잔 후에 event를 설정합니다.
    await asyncio.sleep(1)
    event.set()

    # waiter 태스크가 끝날 때까지 기다립니다.
    await waiter_task

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

이벤트가 설정될 때까지 기다립니다.

이벤트가 설정되었으면 True를 즉시 반환합니다. 그렇지 않으면 다른 태스크가 set()을 호출할 때까지 블록합니다.

set()

이벤트를 설정합니다.

이벤트가 설정되기를 기다리는 모든 태스크는 즉시 깨어납니다.

clear()

이벤트를 지웁니다 (재설정).

set() 메서드가 다시 호출될 때까지 블록됩니다.

is_set()

이벤트가 설정되면 True를 반환합니다.

Condition

class asyncio.Condition(lock=None)

Condition 객체. 스레드 안전하지 않습니다.

asyncio 조건 프리미티브는 태스크가 어떤 이벤트가 발생하기를 기다린 다음 공유 자원에 독점적으로 액세스하는데 사용할 수 있습니다.

본질에서, Condition 객체는 EventLock의 기능을 결합합니다. 여러 개의 Condition 객체가 하나의 Lock을 공유할 수 있으므로, 공유 자원의 특정 상태에 관심이 있는 다른 태스크 간에 공유 자원에 대한 독점적 액세스를 조정할 수 있습니다.

선택적 lock 인자는 Lock 객체나 None 이어야 합니다. 후자의 경우 새로운 Lock 객체가 자동으로 만들어집니다.

버전 3.10에서 변경: loop 매개 변수를 제거했습니다.

Condition을 사용하는 가장 좋은 방법은 async with 문입니다:

cond = asyncio.Condition()

# ... 나중에
async with cond:
    await cond.wait()

이는 다음과 동등합니다:

cond = asyncio.Condition()

# ... 나중에
await cond.acquire()
try:
    await cond.wait()
finally:
    cond.release()
async acquire()

하부 록을 얻습니다.

이 메서드는 하부 록이 풀림(unlocked)이 될 때까지 대기하고, 잠김(locked)으로 설정한 다음 True를 반환합니다.

notify(n=1)

이 조건을 기다리는 n 태스크(기본적으로 1개)를 깨웁니다. 대기 중인 태스크가 n 개 보다 작으면 모두 깨어납니다.

이 메서드를 호출하기 전에 록을 얻어야 하고, 호출 직후에 반납해야 합니다. 풀린(unlocked) 록으로 호출하면 RuntimeError 에러가 발생합니다.

locked()

하부 록을 얻었으면 True를 돌려줍니다.

notify_all()

이 조건에 대기 중인 모든 태스크를 깨웁니다.

이 메서드는 notify()처럼 작동하지만, 대기 중인 모든 태스크를 깨웁니다.

이 메서드를 호출하기 전에 록을 얻어야 하고, 호출 직후에 반납해야 합니다. 풀린(unlocked) 록으로 호출하면 RuntimeError 에러가 발생합니다.

release()

하부 록을 반납합니다.

풀린 록으로 호출하면, RuntimeError가 발생합니다.

async wait()

알릴 때까지 기다립니다.

이 메서드가 호출될 때 호출하는 태스크가 록을 얻지 않았으면 RuntimeError가 발생합니다.

이 메서드는 하부 잠금을 반납한 다음, notify()notify_all() 호출 때문에 깨어날 때까지 블록합니다. 일단 깨어나면, Condition은 록을 다시 얻고, 이 메서드는 True를 돌려줍니다.

작업이 이 호출에서 특이하게 반환될 수 있음에 유의하십시오. 이것이 호출자가 항상 상태를 재확인하고 :meth:`~Condition.wait`를 다시 수행할 준비가 되어 있어야 하는 이유입니다. 이러한 이유로, 대신 :meth:`~Condition.wait_for`를 사용하는 것이 좋습니다.

async wait_for(predicate)

predicate가 이 될 때까지 기다립니다.

predicate는 논릿값으로 해석될 결과를 돌려주는 콜러블이어야 합니다. 메서드는 predicate가 으로 평가될 때까지 반복적으로 wait()합니다. 최종값이 반환 값입니다.

Semaphore

class asyncio.Semaphore(value=1)

Semaphore 객체. 스레드 안전하지 않습니다.

세마포어는 각 acquire() 호출로 감소하고, 각 release() 호출로 증가하는 내부 카운터를 관리합니다. 카운터는 절대로 0 밑으로 내려갈 수 없습니다; acquire()가 0을 만나면, release()를 호출할 때까지 기다리면서 블록합니다.

선택적 value 인자는 내부 카운터의 초깃값을 제공합니다 (기본적으로 1). 지정된 값이 0보다 작으면 ValueError가 발생합니다.

버전 3.10에서 변경: loop 매개 변수를 제거했습니다.

Semaphore를 사용하는 가장 좋은 방법은 async with 문입니다:

sem = asyncio.Semaphore(10)

# ... 나중에
async with sem:
    # 공유 자원으로 작업합니다

이는 다음과 동등합니다:

sem = asyncio.Semaphore(10)

# ... 나중에
await sem.acquire()
try:
    # 공유 자원으로 작업합니다
finally:
    sem.release()
async acquire()

세마포어를 얻습니다.

내부 카운터가 0보다 크면, 1 감소시키고 True를 즉시 반환합니다. 0이면, release()가 호출될 때까지 기다린 다음 True를 반환합니다.

locked()

세마포어를 즉시 얻을 수 없으면 True를 반환합니다.

release()

세마포어를 반납하고 내부 카운터를 1 증가시킵니다. 세마포어를 얻기 위해 대기하는 태스크를 깨울 수 있습니다.

BoundedSemaphore와 달리, Semaphoreacquire() 호출보다 더 많은 release() 호출을 허용합니다.

BoundedSemaphore

class asyncio.BoundedSemaphore(value=1)

제한된 세마포어 객체. 스레드 안전하지 않습니다.

제한된 세마포어는 초기 value 위로 내부 카운터를 증가시키면 release()에서 ValueError를 발생시키는 Semaphore 버전입니다.

버전 3.10에서 변경: loop 매개 변수를 제거했습니다.

Barrier

class asyncio.Barrier(parties)

장벽 객체. 스레드 안전하지 않습니다.

배리어는 parties 개의 작업이 기다릴 때까지 차단할 수 있게 해주는 간단한 동기화 프리미티브입니다. 작업들은 wait() 메서드에서 기다릴 수 있으며, 지정된 수의 작업이 wait() 에서 기다리게 될 때까지 블록됩니다. 이 지점에서, 대기 중이던 모든 작업이 동시에 해제됩니다.

:keyword:`async with`는 :meth:`~Barrier.wait`을 기다리는 대체 수단으로 사용될 수 있습니다.

배리어는 몇 번이라도 재사용될 수 있습니다.

예:

async def example_barrier():
   # 3자 간 장벽
   b = asyncio.Barrier(3)

   # 기다리는 태스크를 2개 만듭니다
   asyncio.create_task(b.wait())
   asyncio.create_task(b.wait())

   await asyncio.sleep(0)
   print(b)

   # 세 번째 .wait() 호출은 장벽을 통과합니다
   await b.wait()
   print(b)
   print("barrier passed")

   await asyncio.sleep(0)
   print(b)

asyncio.run(example_barrier())

이 예제의 결과는 다음과 같습니다:

<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]>

Added in version 3.11.

async wait()

배리어를 통과합니다. 배리어에 참여한 모든 작업들이 이 함수를 호출하면, 모두 동시에 해제됩니다.

배리어에서 대기하거나 블록된 작업이 취소되면, 이 작업은 상태가 동일하게 유지되는 배리어를 빠져나옵니다. 배리어의 상태가 “filling”인 경우, 대기 중인 작업 수가 1 감소합니다.

반환 값은 0부터 parties-1 범위의 정수이며, 작업마다 다릅니다. 이는 특별한 하우스키핑을 수행할 작업을 선택하는 데 사용될 수 있습니다, 예를 들면:

...
async with barrier as position:
   if position == 0:
      # 오직 한 태스크만 이것을 인쇄합니다
      print('End of *draining phase*')

배리어가 작업이 대기하는 동안 손상되거나 재설정되면, 이 메서드는 BrokenBarrierError 예외를 발생시킬 수 있습니다. 작업이 취소되면 :exc:`CancelledError`를 발생시킬 수도 있습니다.

async reset()

배리어를 기본의 빈 상태로 되돌립니다. 그것을 기다리고 있는 모든 작업은 BrokenBarrierError 예외를 수신합니다.

배리어가 손상된 경우 그냥 두고 새것을 만드는 것이 더 좋을 수 있습니다.

async abort()

배리어를 손상된 상태로 만듭니다. 이것은 :meth:`~Barrier.wait`에 대한 모든 활성 또는 미래 호출이 :class:`BrokenBarrierError`로 실패하게 만듭니다. 예를 들어, 무한 대기 작업을 방지하기 위해 작업 중 하나가 중단해야 하는 경우에 이것을 사용하십시오.

parties

배리어를 통과하는 데 필요한 작업 수.

n_waiting

작업이 채워지는 동안 배리어에서 현재 대기 중인 작업 수.

broken

배리어가 손상된 상태이면 True 인 불리언.

exception asyncio.BrokenBarrierError

RuntimeError`의 서브 클래스인 예외는 :class:`Barrier 객체가 재설정되거나 손상될 때 발생합니다.


버전 3.9에서 변경: await lock 이나 yield from lock 및/또는 with 문(with await lock, with (yield from lock))을 사용하여 록을 얻는 것은 제거되었습니다. 대신 async with lock을 사용하십시오.

분실물 보관소