合规国际互联网加速 OSASE为企业客户提供高速稳定SD-WAN国际加速解决方案。 广告
### Navigation - [index](# "General Index") - [modules](# "Python Module Index") | - [next](# "tornado.queues – Queues for coroutines") | - [previous](# "tornado.concurrent — Work with threads and futures") | - [Tornado 4.4.dev1 documentation](#) » - [Coroutines and concurrency](#) » # `tornado.locks` – Synchronization primitives New in version 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`[[source]](#) A condition allows one or more coroutines to wait until notified. Like a standard [`threading.Condition`](https://docs.python.org/3.4/library/threading.html#threading.Condition "(in Python v3.4)") [https://docs.python.org/3.4/library/threading.html#threading.Condition], but does not need an underlying lockthat is acquired and released. With a [`Condition`](# "tornado.locks.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() @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().run_sync(runner) ~~~ ~~~ I'll wait right here About to notify Done notifying I'm done waiting ~~~ [`wait`](# "tornado.locks.Condition.wait") takes an optional `timeout` argument, which is either an absolutetimestamp: ~~~ io_loop = IOLoop.current() # Wait up to 1 second for a notification. yield condition.wait(timeout=io_loop.time() + 1) ~~~ ...or a [`datetime.timedelta`](https://docs.python.org/3.4/library/datetime.html#datetime.timedelta "(in Python v3.4)") [https://docs.python.org/3.4/library/datetime.html#datetime.timedelta] for a timeout relative to the current time: ~~~ # Wait up to 1 second. yield condition.wait(timeout=datetime.timedelta(seconds=1)) ~~~ The method raises [`tornado.gen.TimeoutError`](# "tornado.gen.TimeoutError") if there's no notificationbefore the deadline. `wait`(*timeout=None*)[[source]](#) Wait for [`notify`](# "tornado.locks.Condition.notify"). Returns a [`Future`](# "tornado.concurrent.Future") that resolves `True` if the condition is notified,or `False` after a timeout. `notify`(*n=1*)[[source]](#) Wake `n` waiters. `notify_all`()[[source]](#) Wake all waiters. ### Event *class *`tornado.locks.``Event`[[source]](#) An event blocks coroutines until its internal flag is set to True. Similar to [`threading.Event`](https://docs.python.org/3.4/library/threading.html#threading.Event "(in Python v3.4)") [https://docs.python.org/3.4/library/threading.html#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() @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().run_sync(runner) ~~~ ~~~ Waiting for event About to set the event Not waiting this time Done ~~~ `is_set`()[[source]](#) Return `True` if the internal flag is true. `set`()[[source]](#) Set the internal flag to `True`. All waiters are awakened. Calling [`wait`](# "tornado.locks.Event.wait") once the flag is set will not block. `clear`()[[source]](#) Reset the internal flag to `False`. Calls to [`wait`](# "tornado.locks.Event.wait") will block until [`set`](# "tornado.locks.Event.set") is called. `wait`(*timeout=None*)[[source]](#) Block until the internal flag is true. Returns a Future, which raises [`tornado.gen.TimeoutError`](# "tornado.gen.TimeoutError") after atimeout. ### Semaphore *class *`tornado.locks.``Semaphore`(*value=1*)[[source]](#) A lock that can be acquired a fixed number of times before blocking. A Semaphore manages a counter representing the number of [`release`](# "tornado.locks.Semaphore.release") callsminus the number of [`acquire`](# "tornado.locks.Semaphore.acquire") calls, plus an initial value. The [`acquire`](# "tornado.locks.Semaphore.acquire")method 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: ~~~ from tornado import gen from tornado.ioloop import IOLoop from tornado.locks import Semaphore sem = Semaphore(2) @gen.coroutine def worker(worker_id): 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) ~~~ ~~~ 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`](# "tornado.locks.Semaphore.acquire") is a context manager, so `worker` could 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) ~~~ In Python 3.5, the semaphore itself can be used as an async contextmanager: ~~~ 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) ~~~ Changed in version 4.3: Added `async with` support in Python 3.5. `release`()[[source]](#) Increment the counter and wake one waiter. `acquire`(*timeout=None*)[[source]](#) Decrement the counter. Returns a Future. Block if the counter is zero and wait for a [`release`](# "tornado.locks.Semaphore.release"). The Futureraises [`TimeoutError`](# "tornado.gen.TimeoutError") after the deadline. ### BoundedSemaphore *class *`tornado.locks.``BoundedSemaphore`(*value=1*)[[source]](#) A semaphore that prevents release() being called too many times. If [`release`](# "tornado.locks.BoundedSemaphore.release") would increment the semaphore's value past the initialvalue, it raises [`ValueError`](https://docs.python.org/3.4/library/exceptions.html#ValueError "(in Python v3.4)") [https://docs.python.org/3.4/library/exceptions.html#ValueError]. Semaphores are mostly used to guardresources with limited capacity, so a semaphore released too many timesis a sign of a bug. `release`()[[source]](#) 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`](# "tornado.locks.BoundedSemaphore.release"). The Futureraises [`TimeoutError`](# "tornado.gen.TimeoutError") after the deadline. ### Lock *class *`tornado.locks.``Lock`[[source]](#) A lock for coroutines. A Lock begins unlocked, and [`acquire`](# "tornado.locks.Lock.acquire") locks it immediately. While it islocked, a coroutine that yields [`acquire`](# "tornado.locks.Lock.acquire") waits until another coroutinecalls [`release`](# "tornado.locks.Lock.release"). Releasing an unlocked lock raises [`RuntimeError`](https://docs.python.org/3.4/library/exceptions.html#RuntimeError "(in Python v3.4)") [https://docs.python.org/3.4/library/exceptions.html#RuntimeError]. [`acquire`](# "tornado.locks.Lock.acquire") supports the context manager protocol in all Python versions: ~~~ >>> 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`](# "tornado.locks.Lock") also supports the async context managerprotocol. Note that in this case there is no [`acquire`](# "tornado.locks.Lock.acquire"), because`async with` includes both the `yield` and the `acquire`(just as it does with [`threading.Lock`](https://docs.python.org/3.4/library/threading.html#threading.Lock "(in Python v3.4)") [https://docs.python.org/3.4/library/threading.html#threading.Lock]): ~~~ >>> async def f(): ... async with lock: ... # Do something holding the lock. ... pass ... ... # Now the lock is released. ~~~ Changed in version 4.3: Added `async with` support in Python 3.5. `acquire`(*timeout=None*)[[source]](#) Attempt to lock. Returns a Future. Returns a Future, which raises [`tornado.gen.TimeoutError`](# "tornado.gen.TimeoutError") after atimeout. `release`()[[source]](#) Unlock. The first coroutine in line waiting for [`acquire`](# "tornado.locks.Lock.acquire") gets the lock. If not locked, raise a [`RuntimeError`](https://docs.python.org/3.4/library/exceptions.html#RuntimeError "(in Python v3.4)") [https://docs.python.org/3.4/library/exceptions.html#RuntimeError]. © Copyright 2009-2016, The Tornado Authors. Created using [Sphinx](http://sphinx-doc.org/) 1.3.5.