tornado.locks – Synchronization primitives

4.2 新版功能.

Coordinate coroutines with synchronization primitives analogous to those the standard library provides to threads. These classes are very similar to those provided in the standard library’s asyncio package.

警告

Note that these primitives are not actually thread-safe and cannot be used in place of those from the standard library–they are meant to coordinate Tornado coroutines in a single-threaded app, not to protect shared objects in a multithreaded app.

Condition

class tornado.locks.Condition[源代码]

A condition allows one or more coroutines to wait until notified.

Like a standard threading.Condition, but does not need an underlying lock that is acquired and released.

With a Condition, coroutines can wait to be notified by other coroutines:

from tornado import gen
from tornado.ioloop import IOLoop
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()])

IOLoop.current().run_sync(runner)
I'll wait right here
About to notify
Done notifying
I'm done waiting

wait takes an optional timeout argument, which is either an absolute timestamp:

io_loop = IOLoop.current()

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

…or a datetime.timedelta for a timeout relative to the current time:

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

The method returns False if there’s no notification before the deadline.

在 5.0 版更改: Previously, waiters could be notified synchronously from within notify. Now, the notification will always be received on the next iteration of the IOLoop.

wait(timeout=None)[源代码]

Wait for notify.

Returns a Future that resolves True if the condition is notified, or False after a timeout.

notify(n=1)[源代码]

Wake n waiters.

notify_all()[源代码]

Wake all waiters.

Event

class tornado.locks.Event[源代码]

An event blocks coroutines until its internal flag is set to True.

Similar to threading.Event.

A coroutine can wait for an event to be set. Once it is set, calls to yield event.wait() will not block unless the event has been cleared:

from tornado import gen
from tornado.ioloop import IOLoop
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()])

IOLoop.current().run_sync(runner)
Waiting for event
About to set the event
Not waiting this time
Done
is_set()[源代码]

Return True if the internal flag is true.

set()[源代码]

Set the internal flag to True. All waiters are awakened.

Calling wait once the flag is set will not block.

clear()[源代码]

Reset the internal flag to False.

Calls to wait will block until set is called.

wait(timeout=None)[源代码]

Block until the internal flag is true.

Returns a Future, which raises tornado.util.TimeoutError after a timeout.

Semaphore

class tornado.locks.Semaphore(value=1)[源代码]

A lock that can be acquired a fixed number of times before blocking.

A Semaphore manages a counter representing the number of release calls minus the number of acquire calls, plus an initial value. The acquire method blocks if necessary until it can return without making the counter negative.

Semaphores limit access to a shared resource. To allow access for two workers at a time:

from tornado import gen
from tornado.ioloop import IOLoop
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)])

IOLoop.current().run_sync(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

Workers 0 and 1 are allowed to run concurrently, but worker 2 waits until the semaphore has been released once, by worker 0.

The semaphore can be used as an async context manager:

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)

For compatibility with older versions of Python, acquire is a context manager, so worker could also be written as:

@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 版更改: Added async with support in Python 3.5.

release()[源代码]

Increment the counter and wake one waiter.

acquire(timeout=None)[源代码]

Decrement the counter. Returns a Future.

Block if the counter is zero and wait for a release. The Future raises TimeoutError after the deadline.

BoundedSemaphore

class tornado.locks.BoundedSemaphore(value=1)[源代码]

A semaphore that prevents release() being called too many times.

If release would increment the semaphore’s value past the initial value, it raises ValueError. Semaphores are mostly used to guard resources with limited capacity, so a semaphore released too many times is a sign of a bug.

release()[源代码]

Increment the counter and wake one waiter.

acquire(timeout=None)

Decrement the counter. Returns a Future.

Block if the counter is zero and wait for a release. The Future raises TimeoutError after the deadline.

Lock

class tornado.locks.Lock[源代码]

A lock for coroutines.

A Lock begins unlocked, and acquire locks it immediately. While it is locked, a coroutine that yields acquire waits until another coroutine calls release.

Releasing an unlocked lock raises RuntimeError.

A Lock can be used as an async context manager with the async with statement:

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

For compatibility with older versions of Python, the acquire method asynchronously returns a regular context manager:

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

在 4.3 版更改: Added async with support in Python 3.5.

acquire(timeout=None)[源代码]

Attempt to lock. Returns a Future.

Returns a Future, which raises tornado.util.TimeoutError after a timeout.

release()[源代码]

Unlock.

The first coroutine in line waiting for acquire gets the lock.

If not locked, raise a RuntimeError.