# -*- coding: utf-8 -*- import time from threading import Lock, RLock from datetime import datetime from threading import Thread import threading class Test: def __init__(self): self.obj_lock = Lock() self.obj_rlock = RLock() self.a = 1 self.b = 2 self.r = 'r' def t(self): print('123') def a(self): with self.obj_lock: print("a") self.b() def b(self): with self.obj_lock: print("b") def ar(self): with self.obj_rlock: print("ar") self.r = 'ar' self.br() def br(self): with self.obj_rlock: print("br") self.r = 'br' t = Test() print(t.a) t.t() t.ar() # t.br() print('t.r', t.r)
https://github.com/python/cpython/blob/master/Doc/library/contextlib.rst
https://github.com/python/cpython/blob/master/Doc/library/contextlib.rst#reentrant-context-managers
Reentrant context managers
More sophisticated context managers may be "reentrant". These context managers can not only be used in multiple :keyword:`with` statements, but may also be used inside a :keyword:`!with` statement that is already using the same context manager.
:class:`threading.RLock` is an example of a reentrant context manager, as are :func:`suppress` and :func:`redirect_stdout`. Here's a very simple example of reentrant use:
>>> from contextlib import redirect_stdout >>> from io import StringIO >>> stream = StringIO() >>> write_to_stream = redirect_stdout(stream) >>> with write_to_stream: ... print("This is written to the stream rather than stdout") ... with write_to_stream: ... print("This is also written to the stream") ... >>> print("This is written directly to stdout") This is written directly to stdout >>> print(stream.getvalue()) This is written to the stream rather than stdout This is also written to the stream
Real world examples of reentrancy are more likely to involve multiple functions calling each other and hence be far more complicated than this example.
Note also that being reentrant is not the same thing as being thread safe. :func:`redirect_stdout`, for example, is definitely not thread safe, as it makes a global modification to the system state by binding :data:`sys.stdout` to a different stream.
https://docs.python.org/zh-cn/3/library/threading.html#rlock-objects
重入锁是一个可以被同一个线程多次获取的同步基元组件。在内部,它在基元锁的锁定/非锁定状态上附加了 "所属线程" 和 "递归等级" 的概念。在锁定状态下,某些线程拥有锁 ; 在非锁定状态下, 没有线程拥有它。
acquire
(blocking=True, timeout=-1)-
可以阻塞或非阻塞地获得锁。
当无参数调用时: 如果这个线程已经拥有锁,递归级别增加一,并立即返回。否则,如果其他线程拥有该锁,则阻塞至该锁解锁。一旦锁被解锁(不属于任何线程),则抢夺所有权,设置递归等级为一,并返回。如果多个线程被阻塞,等待锁被解锁,一次只有一个线程能抢到锁的所有权。在这种情况下,没有返回值。
当调用时参数 blocking 设置为
True
,和没带参数调用一样做同样的事,然后返回True
。当 blocking 参数设置为 false 的情况下调用,不进行阻塞。如果一个无参数的调用已经阻塞,立即返回false;否则,执行和无参数调用一样的操作,并返回true。
当浮点数 timeout 参数被设置为正值调用时,只要无法获得锁,将最多阻塞 timeout 设定的秒数。 如果锁被获取返回 true,如果超时返回false。
在 3.2 版更改: 新的 timeout 形参。
release
()-
释放锁,自减递归等级。如果减到零,则将锁重置为非锁定状态(不被任何线程拥有),并且,如果其他线程正被阻塞着等待锁被解锁,则仅允许其中一个线程继续。如果自减后,递归等级仍然不是零,则锁保持锁定,仍由调用线程拥有。
只有当前线程拥有锁才能调用这个方法。如果锁被释放后调用这个方法,会引起
RuntimeError
异常。没有返回值。
原始锁是一个在锁定时不属于特定线程的同步基元组件。在Python中,它是能用的最低级的同步基元组件,由 _thread
扩展模块直接实现。
、
该模块的设计基于 Java的线程模型。 但是,在Java里面,锁和条件变量是每个对象的基础特性,而在Python里面,这些被独立成了单独的对象。 Python 的 Thread
类只是 Java 的 Thread 类的一个子集;目前还没有优先级,没有线程组,线程还不能被销毁、停止、暂停、恢复或中断。 Java 的 Thread 类的静态方法在实现时会映射为模块级函数。
下列描述的方法都是自动执行的。
https://docs.python.org/zh-cn/3/library/threading.html#thread-local-data
https://tech.meituan.com/2018/11/15/java-lock.html
cpython/blob/master/Modules/_threadmodule.c
拥有的条件 static PyObject * rlock_is_owned(rlockobject *self, PyObject *Py_UNUSED(ignored)) { unsigned long tid = PyThread_get_thread_ident(); if (self->rlock_count > 0 && self->rlock_owner == tid) { Py_RETURN_TRUE; } Py_RETURN_FALSE; } 创建 static PyObject * rlock_new(PyTypeObject *type, PyObject *args, PyObject *kwds) { rlockobject *self; self = (rlockobject *) type->tp_alloc(type, 0); if (self != NULL) { self->in_weakreflist = NULL; self->rlock_owner = 0; self->rlock_count = 0; self->rlock_lock = PyThread_allocate_lock(); if (self->rlock_lock == NULL) { Py_DECREF(self); PyErr_SetString(ThreadError, "can't allocate lock"); return NULL; } } return (PyObject *) self; } static PyObject * lock_PyThread_acquire_lock(lockobject *self, PyObject *args, PyObject *kwds) { _PyTime_t timeout; PyLockStatus r; if (lock_acquire_parse_args(args, kwds, &timeout) < 0) return NULL; r = acquire_timed(self->lock_lock, timeout); if (r == PY_LOCK_INTR) { return NULL; } if (r == PY_LOCK_ACQUIRED) self->locked = 1; return PyBool_FromLong(r == PY_LOCK_ACQUIRED); }
如果多线程的场景下,没有在一个线程内的 锁的获取与释放的要求,那么 Rlock的作用 就是否和Lock一样?1
单线程的情况下,lock有的功能点,Rlock也有,反之不成立;
多线程的情况下, 如果有在同一线程内的锁的释放或获取的需求,则用Rlock; 否则,用Lock,因为Rlock的运行逻辑多于Lock,会有更多的资源消耗 性能开销。