aiorwlock: Read Write Lock for Asyncio
raw JSON → 1.5.1 verified Wed May 13 auth: no python
aiorwlock provides a read-write lock, a synchronization primitive for `asyncio` applications. It allows multiple reader tasks to hold a lock concurrently, while a writer task obtains an exclusive lock, blocking all readers and other writers. This pattern is ideal for resources that are frequently read but infrequently written. The library is currently at version 1.5.1 and maintains an active release cadence with several minor and patch updates per year.
pip install aiorwlock Common errors
error ModuleNotFoundError: No module named 'aiorwlock' ↓
cause The 'aiorwlock' module is not installed in the Python environment.
fix
pip install aiorwlock
error AttributeError: module 'aiorwlock' has no attribute 'RWLock' ↓
cause The 'RWLock' class is not found in the 'aiorwlock' module, possibly due to an incorrect import statement.
fix
from aiorwlock import RWLock
error RuntimeError: Task got Future <Future pending> attached to a different loop ↓
cause An attempt was made to use 'aiorwlock' across different asyncio event loops, which is not supported.
fix
Ensure that the 'RWLock' instance is used within the same asyncio event loop where it was created.
error RuntimeError: A task that acquires the lock should be used for releasing it. ↓
cause The read-write lock was acquired by one asyncio task but attempted to be released by a different task, which is not permitted by `aiorwlock`'s design.
fix
Ensure that the
RWLock's reader or writer lock is acquired and released within the same asyncio task, typically by using async with statements or by calling acquire() and release() from the same coroutine. error RuntimeWarning: coroutine 'RWLock.reader_lock' was never awaited ↓
cause You are calling an asynchronous method or accessing an awaitable context manager (like `rwlock.reader_lock` or `rwlock.writer_lock`) but are not using the `await` keyword or `async with` statement to schedule or enter it.
fix
Prepend
await when calling acquire() or use async with when entering the reader/writer lock context. For example, async with rwlock.reader_lock: or await rwlock.reader_lock.acquire(). Warnings
breaking Python 3.7 support was dropped in v1.4.0. Users on Python 3.7 or older must upgrade their Python version to use `aiorwlock` v1.4.0 or newer. ↓
fix Upgrade Python to 3.8 or newer. The current minimum supported version is Python 3.9.
breaking The explicit `loop` parameter for the `RWLock` constructor was deprecated in v1.0.0 and removed in v1.3.0. Passing a `loop` argument will now raise an error. ↓
fix Remove the `loop` argument from `RWLock()` constructor calls. `aiorwlock` now lazily evaluates the current event loop.
deprecated Creation of `RWLock` instances outside of an `async` function context was deprecated in v1.0.0. While it might still work in some setups due to lazy loop evaluation, it's considered bad practice. ↓
fix Ensure `RWLock()` instances are created within an `async` function or a context where `asyncio.get_running_loop()` can correctly resolve the event loop.
gotcha Fixed a cross-event-loop race condition in lock acquisition and a deadlock that could occur when tasks are cancelled. ↓
fix Upgrade to `aiorwlock` v1.5.1 or newer to benefit from critical stability fixes.
gotcha Fixed a bug that allowed concurrent writes under rare conditions. ↓
fix Upgrade to `aiorwlock` v1.2.0 or newer to ensure correct exclusive write lock behavior.
gotcha A `RuntimeError` will be raised if a lock is acquired by one task but released by another. Synchronization primitives in `asyncio` are typically task-bound. ↓
fix Always ensure that the same `async` task (coroutine) that acquires a `reader_lock` or `writer_lock` is also responsible for releasing it (e.g., by using `async with`).
Install compatibility last tested: 2026-05-13 v1.5.1 (up to date)
python os / libc status wheel install import disk mem side effects
3.10 alpine (musl) wheel - 0.08s 17.8M 4.0M clean
3.10 alpine (musl) - - 0.12s 17.8M 4.0M -
3.10 slim (glibc) wheel 1.4s 0.06s 18M 4.0M clean
3.10 slim (glibc) - - 0.09s 18M 4.0M -
3.11 alpine (musl) wheel - 0.15s 19.6M 4.9M clean
3.11 alpine (musl) - - 0.21s 19.6M 4.9M -
3.11 slim (glibc) wheel 1.6s 0.13s 20M 4.9M clean
3.11 slim (glibc) - - 0.18s 20M 4.9M -
3.12 alpine (musl) wheel - 0.34s 11.5M 8.2M clean
3.12 alpine (musl) - - 0.64s 11.5M 8.2M -
3.12 slim (glibc) wheel 1.4s 0.31s 12M 8.2M clean
3.12 slim (glibc) - - 0.45s 12M 8.2M -
3.13 alpine (musl) wheel - 0.37s 11.3M 8.7M clean
3.13 alpine (musl) - - 0.57s 11.2M 8.7M -
3.13 slim (glibc) wheel 1.4s 0.32s 12M 8.7M clean
3.13 slim (glibc) - - 0.60s 12M 8.7M -
3.9 alpine (musl) wheel - 0.07s 17.3M 3.9M clean
3.9 alpine (musl) - - 0.12s 17.3M 3.9M -
3.9 slim (glibc) wheel 1.7s 0.07s 18M 3.9M clean
3.9 slim (glibc) - - 0.11s 18M 3.9M -
Imports
- RWLock
from aiorwlock import RWLock
Quickstart last tested: 2026-04-25
import asyncio
from aiorwlock import RWLock
async def reader_task(rwlock: RWLock, reader_id: int):
print(f"Reader {reader_id}: Attempting to acquire read lock")
async with rwlock.reader_lock:
print(f"Reader {reader_id}: Acquired read lock")
await asyncio.sleep(0.05) # Simulate reading
print(f"Reader {reader_id}: Released read lock")
async def writer_task(rwlock: RWLock, writer_id: int):
print(f"Writer {writer_id}: Attempting to acquire write lock")
async with rwlock.writer_lock:
print(f"Writer {writer_id}: Acquired write lock")
await asyncio.sleep(0.1) # Simulate writing
print(f"Writer {writer_id}: Released write lock")
async def main():
rwlock = RWLock()
tasks = []
# Start some readers
for i in range(3):
tasks.append(asyncio.create_task(reader_task(rwlock, i)))
# Start a writer in between
tasks.append(asyncio.create_task(writer_task(rwlock, 100)))
# Start more readers
for i in range(3, 6):
tasks.append(asyncio.create_task(reader_task(rwlock, i)))
await asyncio.gather(*tasks)
if __name__ == "__main__":
asyncio.run(main())