tornado.locks – 同期プリミティブ

バージョン 4.2 で追加。

標準ライブラリがスレッドに提供するものと同様の同期プリミティブを使用して、コルーチンを調整します。これらのクラスは、標準ライブラリのasyncio パッケージで提供されているものと非常によく似ています。

警告

これらのプリミティブは実際にはスレッドセーフではなく、標準ライブラリのthreadingモジュールのものとは置き換えられません。これらは、マルチスレッドアプリケーションで共有オブジェクトを保護するためではなく、シングルスレッドアプリケーションでTornadoコルーチンを調整することを目的としています。

Condition

class tornado.locks.Condition[source]

Condition は、1つ以上のコルーチンが通知されるまで待機できるようにします。

標準のthreading.Conditionと似ていますが、取得および解放される基盤となるロックは必要ありません。

Conditionを使用すると、コルーチンは他のコルーチンによって通知されるのを待つことができます。

import asyncio
from tornado import gen
from tornado.locks import Condition

condition = Condition()

async def waiter():
    print("I'll wait right here")
    await condition.wait()
    print("I'm done waiting")

async def notifier():
    print("About to notify")
    condition.notify()
    print("Done notifying")

async def runner():
    # Wait for waiter() and notifier() in parallel
    await gen.multi([waiter(), notifier()])

asyncio.run(runner())
I'll wait right here
About to notify
Done notifying
I'm done waiting

waitは、オプションのtimeout引数を取ります。これは絶対的なタイムスタンプです。

io_loop = IOLoop.current()

# Wait up to 1 second for a notification.
await condition.wait(timeout=io_loop.time() + 1)

…または、現在の時刻からの相対的なタイムアウトのためのdatetime.timedeltaです。

# Wait up to 1 second.
await condition.wait(timeout=datetime.timedelta(seconds=1))

期限前に通知がない場合は、Falseを返します。

バージョン 5.0 で変更: 以前は、ウェイターはnotify内から同期的に通知される可能性がありました。現在は、通知は常にIOLoopの次の反復で受信されます。

wait(timeout: Optional[Union[float, timedelta]] = None) Awaitable[bool][source]

notifyを待ちます。

Conditionが通知された場合はTrue、タイムアウト後はFalseを解決するFutureを返します。

notify(n: int = 1) None[source]

n個のウェイターを起こします。

notify_all() None[source]

すべてのウェイターを起こします。

Event

class tornado.locks.Event[source]

イベントは、内部フラグがTrueに設定されるまでコルーチンをブロックします。

threading.Eventに似ています。

コルーチンは、イベントが設定されるのを待つことができます。設定されると、yield event.wait()への呼び出しは、イベントがクリアされない限りブロックされません。

import asyncio
from tornado import gen
from tornado.locks import Event

event = Event()

async def waiter():
    print("Waiting for event")
    await event.wait()
    print("Not waiting this time")
    await event.wait()
    print("Done")

async def setter():
    print("About to set the event")
    event.set()

async def runner():
    await gen.multi([waiter(), setter()])

asyncio.run(runner())
Waiting for event
About to set the event
Not waiting this time
Done
is_set() bool[source]

内部フラグがtrueの場合はTrueを返します。

set() None[source]

内部フラグをTrueに設定します。すべてのウェイターが起動されます。

waitをフラグが設定された後に呼び出すと、ブロックされません。

clear() None[source]

内部フラグをFalseにリセットします。

waitへの呼び出しは、setが呼び出されるまでブロックされます。

wait(timeout: Optional[Union[float, timedelta]] = None) Awaitable[None][source]

内部フラグがtrueになるまでブロックします。

tornado.util.TimeoutErrorをタイムアウト後に発生させるawaitableを返します。

Semaphore

class tornado.locks.Semaphore(value: int = 1)[source]

ブロックする前に一定回数取得できるロック。

Semaphoreは、release呼び出しの数からacquire呼び出しの数を引いた数、プラス初期値を表すカウンタを管理します。acquireメソッドは、カウンタを負にすることなく返ることができるまで必要に応じてブロックします。

Semaphoreは、共有リソースへのアクセスを制限します。2つのワーカーが同時にアクセスできるようにするには

import asyncio
from tornado import gen
from tornado.locks import Semaphore

sem = Semaphore(2)

async def worker(worker_id):
    await sem.acquire()
    try:
        print("Worker %d is working" % worker_id)
        await use_some_resource()
    finally:
        print("Worker %d is done" % worker_id)
        sem.release()

async def runner():
    # Join all workers.
    await gen.multi([worker(i) for i in range(3)])

asyncio.run(runner())
Worker 0 is working
Worker 1 is working
Worker 0 is done
Worker 2 is working
Worker 1 is done
Worker 2 is done

ワーカー0と1は同時に実行できますが、ワーカー2はワーカー0によってセマフォが一度解放されるまで待ちます。

セマフォは非同期コンテキストマネージャとして使用できます。

async def worker(worker_id):
    async with sem:
        print("Worker %d is working" % worker_id)
        await use_some_resource()

    # Now the semaphore has been released.
    print("Worker %d is done" % worker_id)

古いバージョンのPythonとの互換性のために、acquireはコンテキストマネージャであるため、workerは次のように書くこともできます。

@gen.coroutine
def worker(worker_id):
    with (yield sem.acquire()):
        print("Worker %d is working" % worker_id)
        yield use_some_resource()

    # Now the semaphore has been released.
    print("Worker %d is done" % worker_id)

バージョン4.3で変更: Python 3.5でasync withサポートを追加。

release() None[source]

カウンタを増分し、1つのウェイターをウェイクアップします。

acquire(timeout: Optional[Union[float, timedelta]] = None) Awaitable[_ReleasingContextManager][source]

カウンタをデクリメントします。awaitableを返します。

カウンタがゼロの場合、ブロックし、releaseを待ちます。awaitableは、期限切れ後にTimeoutErrorを発生させます。

BoundedSemaphore

class tornado.locks.BoundedSemaphore(value: int = 1)[source]

release()が何回も呼び出されるのを防ぐセマフォ。

releaseがセマフォの値を初期値を超えて増分しようとすると、ValueErrorを発生させます。セマフォは主に容量が限られたリソースを保護するために使用されるため、セマフォが何回も解放されることはバグの兆候です。

release() None[source]

カウンタを増分し、1つのウェイターをウェイクアップします。

acquire(timeout: Optional[Union[float, timedelta]] = None) Awaitable[_ReleasingContextManager]

カウンタをデクリメントします。awaitableを返します。

カウンタがゼロの場合、ブロックし、releaseを待ちます。awaitableは、期限切れ後にTimeoutErrorを発生させます。

Lock

class tornado.locks.Lock[source]

コルーチン用のロック。

Lockはロック解除状態で開始され、acquireによってすぐにロックされます。ロックされている間、acquireをyieldするコルーチンは、別のコルーチンがreleaseを呼び出すまで待ちます。

ロックされていないロックを解放しようとすると、RuntimeErrorが発生します。

Lockは、async withステートメントを使用して非同期コンテキストマネージャとして使用できます。

>>> from tornado import locks
>>> lock = locks.Lock()
>>>
>>> async def f():
...    async with lock:
...        # Do something holding the lock.
...        pass
...
...    # Now the lock is released.

古いバージョンのPythonとの互換性のために、acquireメソッドは非同期的に通常のコンテキストマネージャを返します。

>>> async def f2():
...    with (yield lock.acquire()):
...        # Do something holding the lock.
...        pass
...
...    # Now the lock is released.

バージョン4.3で変更: Python 3.5でasync withサポートを追加。

acquire(timeout: Optional[Union[float, timedelta]] = None) Awaitable[_ReleasingContextManager][source]

ロックを試みます。awaitableを返します。

tornado.util.TimeoutErrorをタイムアウト後に発生させるawaitableを返します。

release() None[source]

ロックを解除します。

acquireを待っているコルーチンの中で最初に来たものがロックを取得します。

ロックされていない場合、RuntimeErrorを発生させます。