python threading lock和rlock_Python中可重入锁(RLock)的理解

版本:Python 3.7.0

系统:win10 64

上一篇文章,我们知道了threading模块中lock、lock.acquire()、lock.release()的实现原理:利用机器指令保证“上锁的过程”原子化,当锁被某个线程持有时,其他线程再来获取,就会处于忙等状态,那么reentrant lock 是如何保证:一个锁可以被一个线程访问多次,不会处于忙等状态?

一个锁可以被一个线程访问多次,不会处于死锁状态?

如果是一把互斥锁(threading.Lock()),那么下面的代码会发生堵塞:

import threading

lock = threading.Lock()

lock.acquire()

for i in range(10):

print('获取第二把锁')

lock.acquire()

print(f'test.......{i}')

lock.release()

lock.release()

同样的代码,如果换成(threading.RLock()),则不会发生堵塞:

import threading

lock = threading.RLock()

lock.acquire()

for i in range(10):

print('获取第二把锁')

lock.acquire()

print(f'test.......{i}')

lock.release()

lock.release()

可能我们大部分人都知道,RLock其实底层维护了一个互斥锁和一个计数器,那互斥锁和计数器到底是如何工作的?我们从RLock的实现源码一探究竟。

在threading内部,RLock实现方式有两种,一种是调用_thread模块下的RLock,它是用C语言写的,另外一种是用Python语言写的,不管哪种方式,其实现原理是一致的。

[threading]

def RLock(*args, **kwargs):

if _CRLock is None:

return _PyRLock(*args, **kwargs) # python语言实现的RLock

return _CRLock(*args, **kwargs) # _thread模块中RLock,C语言实现的

Python语言实现的RLock源码如下(C语言实现的RLock可见文末):

[threading]

class _RLock:

def __init__(self):

self._block = _allocate_lock() # _thread模块中定义一个锁对象的方法

self._owner = None # 用来标记哪个线程获取了锁

self._count = 0 # 计数器

def acquire(self, blocking=True, timeout=-1):

me = get_ident()

if self._owner == me:

self._count += 1

return 1

rc = self._block.acquire(blocking, timeout)

if rc:

self._owner = me

self._count = 1

return rc

def release(self):

if self._owner != get_ident():

raise RuntimeError("cannot release un-acquired lock")

self._count = count = self._count - 1

if not count:

self._owner = None

self._block.release()

acquire()方法的流程图:

结合acquire()方法的源码和流程图,可以清楚的得知,当一个线程通过acquire()获取一个锁时,首先会判断拥有锁的线程和调用acquire()的线程是否是同一个线程,如果是同一个线程,那么计数器+1,函数直接返回(return 1),如果两个线程不一致时,那么会通过调用底层锁(_allocate_lock())进行阻塞自己(也可能是获得锁)。

那到这里,文章开头所提的问题,就迎刃而解了:当某个线程内部多次调用可重入锁时,仅仅在第一次获取锁对象时调用了_thread模块中锁的acquire()方法,第二次,第三次...只是让计数器加1了而已;而当其他线程获取该锁时,因为调用了 _trhead模块中 _allocate_lock()方法阻塞了自己。

C语言实现的RLock:

[_threadmodule.c]

typedef struct {

PyObject_HEAD

PyThread_type_lock rlock_lock;

unsigned long rlock_owner;

unsigned long rlock_count;

PyObject *in_weakreflist;

} rlockobject;

static PyObject *

rlock_acquire(rlockobject *self, PyObject *args, PyObject *kwds)

{

_PyTime_t timeout;

unsigned long tid;

PyLockStatus r = PY_LOCK_ACQUIRED;

if (lock_acquire_parse_args(args, kwds, &timeout) < 0)

return NULL;

tid = PyThread_get_thread_ident();

if (self->rlock_count > 0 && tid == self->rlock_owner) {

unsigned long count = self->rlock_count + 1;

if (count <= self->rlock_count) {

PyErr_SetString(PyExc_OverflowError,

"Internal lock count overflowed");

return NULL;

}

self->rlock_count = count;

Py_RETURN_TRUE;

}

r = acquire_timed(self->rlock_lock, timeout);

if (r == PY_LOCK_ACQUIRED) {

assert(self->rlock_count == 0);

self->rlock_owner = tid;

self->rlock_count = 1;

}

else if (r == PY_LOCK_INTR) {

return NULL;

}

return PyBool_FromLong(r == PY_LOCK_ACQUIRED);

}

水平有限,欢迎批评指正,欢迎交流!

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值