tornado.locks – Synchronization primitives¶

4.2 新版功能.

Coordinate coroutines with synchronization primitives analogous to those thestandard library provides to threads.

(Note that these primitives are not actually thread-safe and cannot be used inplace of those from the standard library–they are meant to coordinate Tornadocoroutines in a single-threaded app, not to protect shared objects in amultithreaded 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 lockthat is acquired and released.

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



  1. from tornado import gen
    from tornado.ioloop import IOLoop
    from tornado.locks import Condition

    condition = Condition()

    @gen.coroutine
    def waiter():
    print("I'll wait right here")
    yield condition.wait() # Yield a Future.
    print("I'm done waiting")

    @gen.coroutine
    def notifier():
    print("About to notify")
    condition.notify()
    print("Done notifying")

    @gen.coroutine
    def runner():
    # Yield two Futures; wait for waiter() and notifier() to finish.
    yield [waiter(), notifier()]

    IOLoop.current().runsync(runner)






  1. I'll wait right here
    About to notify
    Done notifying
    I'm done waiting




wait takes an optional timeout argument, which is either an absolutetimestamp:



  1. io_loop = IOLoop.current()

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




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



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




The method raises tornado.gen.TimeoutError if there’s no notificationbefore the deadline.
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 toyield event.wait() will not block unless the event has been cleared:



  1. from tornado import gen
    from tornado.ioloop import IOLoop
    from tornado.locks import Event

    event = Event()

    @gen.coroutine
    def waiter():
    print("Waiting for event")
    yield event.wait()
    print("Not waiting this time")
    yield event.wait()
    print("Done")

    @gen.coroutine
    def setter():
    print("About to set the event")
    event.set()

    @gen.coroutine
    def runner():
    yield [waiter(), setter()]

    IOLoop.current().runsync(runner)






  1. 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.gen.TimeoutError after atimeout.

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 callsminus the number of acquire calls, plus an initial value. The acquiremethod blocks if necessary until it can return without making the counternegative.

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



  1. from tornado import gen
    from tornado.ioloop import IOLoop
    from tornado.locks import Semaphore

    sem = Semaphore(2)

    @gen.coroutine
    def worker(workerid):
    yield sem.acquire()
    try:
    print("Worker %d is working" % worker_id)
    yield use_some_resource()
    finally:
    print("Worker %d is done" % worker_id)
    sem.release()

    @gen.coroutine
    def runner():
    # Join all workers.
    yield [worker(i) for i in range(3)]

    IOLoop.current().run_sync(runner)






  1. 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 untilthe semaphore has been released once, by worker 0.

acquire is a context manager, so worker could be written as:



  1. @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)




In Python 3.5, the semaphore itself can be used as an async contextmanager:



  1. 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)





在 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 Futureraises 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 initialvalue, it raises ValueError. Semaphores are mostly used to guardresources with limited capacity, so a semaphore released too many timesis 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 Futureraises TimeoutError after the deadline.

Lock¶

class tornado.locks.Lock[源代码]

A lock for coroutines.

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

Releasing an unlocked lock raises RuntimeError.

acquire supports the context manager protocol in all Python versions:



  1. >>> from tornado import gen, locks
    >>> lock = locks.Lock()
    >>>
    >>> @gen.coroutine
    def f():
    with (yield lock.acquire()):
    # Do something holding the lock.
    pass

    # Now the lock is released.




In Python 3.5, Lock also supports the async context managerprotocol. Note that in this case there is no acquire, becauseasync with includes both the yield and the acquire(just as it does with threading.Lock):



  1. >>> async def f():
    async with lock:
    # 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.gen.TimeoutError after atimeout.
release()[源代码]

Unlock.

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

If not locked, raise a RuntimeError.

原文:

https://tornado-zh-cn.readthedocs.io/zh_CN/latest/locks.html