1. 本际云推荐 - 专业推荐VPS、服务器,IDC点评首页
  2. 云主机运维
  3. VPS运维

Python并行编程多线程锁机制Lock与RLock实现线程同步

Python多线程编程中的锁机制

作为一门比较常见的编程语言,Python可以对其进行多线程的编程,包括利用Lock与RLock,实现多线程之间的相互同步,那么,实现这种原理的机制到底是什么样子的呢?下面就给大家详细解答下。

Python并行编程多线程锁机制Lock与RLock实现线程同步

什么是锁机制?

要回答这个问题,我们需要知道为什么需要使用锁机制。前面我们谈到一个进程内的多个线程的某些资源是共享的,这也是线程的一大优势,但是也随之带来一个问题,即当两个及两个以上的线程同时访问共享资源时,如果此时没有预设对应的同步机制,就可能带来同一时刻多个线程同时访问同一个共享资源,即出现竞态,多数情况下我们是不希望出现这样的情况的,那么怎么避免呢?

Lock()管理线程

通过实现对资源的加锁,我们可以保证在一个时刻只有一个线程可以访问资源,这就需要利用Lock()。在此之前,我们先看一段代码:

import threading
import time
resource=0
count=1000000
resource_lock=threading.Lock()
def increment():
global resource
for i in range(count):
resource+=1
def decerment():
global resource
for i in range(count):
resource-=1
increment_thread=threading.Thread(target=increment)
decerment_thread=threading.Thread(target=decerment)
increment_thread.start()
decerment_thread.start()
increment_thread.join()
decerment_thread.join()
print(resource)

运行截图如下:

lianshib-1.png

当我们多次运行时,可以看到最终的结果都几乎不等于我们期待的值即resource初始值0。

原因就是因为+=和-=并不是原子操作。可以使用dis模块查看字节码:

import dis
def add(total):
total+=1
def desc(total):
total-=1
total=0
print(dis.dis(add))
print(dis.dis(desc))
#运行结果:
#3 0 LOAD_FAST 0(total)
#3 LOAD_CONST 1(1)
#6 INPLACE_ADD
#7 STORE_FAST 0(total)
#10 LOAD_CONST 0(None)
#13 RETURN_VALUE
#None
#5 0 LOAD_FAST 0(total)
#3 LOAD_CONST 1(1)
#6 INPLACE_SUBTRACT
#7 STORE_FAST 0(total)
#10 LOAD_CONST 0(None)
#13 RETURN_VALUE
#None

那么如何保证初始值为0呢?我们可以利用Lock(),代码如下:

import threading
import time
resource=0
count=1000000
resource_lock=threading.Lock()
def increment():
global resource
for i in range(count):
resource_lock.acquire()
resource+=1
resource_lock.release()
def decerment():
global resource
for i in range(count):
resource_lock.acquire()
resource-=1
resource_lock.release()
increment_thread=threading.Thread(target=increment)
decerment_thread=threading.Thread(target=decerment)
increment_thread.start()
decerment_thread.start()
increment_thread.join()
decerment_thread.join()
print(resource)

运行截图如下:

lianshib-2.png

从运行结果可以看到,不论我们运行多少次该代码,其resource的值都为初始值0,这就是Lock()的功劳,即它可以将某一时刻的访问限定在单个线程或者单个类型的线程上,在访问锁定的共享资源时,必须要现获取对应的锁才能访问,即要等待其他线程释放资源,即resource_lock.release()。为防止对某个资源锁定后,忘记释放锁导致死锁,我们可以利用上下文管理器管理锁实现同样的效果:

import threading
import time
resource=0
count=1000000
resource_lock=threading.Lock()
def increment():
global resource
for i in range(count):
with resource_lock:
resource+=1
def decerment():
global resource
for i in range(count):
with resource_lock:
resource-=1
increment_thread=threading.Thread(target=increment)
decerment_thread=threading.Thread(target=decerment)
increment_thread.start()
decerment_thread.start()

RLock()与Lock()的区别

Lock()作为一个基本的锁对象,一次只能一个锁定,其余锁请求,需等待锁释放后才能获取,否则会出现死锁。为解决同一线程中不能多次请求同一资源的问题,Python提供了“可重入锁”,即threading.RLock()。RLock内部维护着一个Lock和一个counter变量,counter记录了acquire的次数,从而使得资源可以被多次acquire。直到一个线程所有的acquire都被release,其他的线程才能获得资源。比如递归锁的使用:

import threading
lock=threading.RLock()
def dosomething(lock):
lock.acquire()
#do something
lock.release()
lock.acquire()
dosomething(lock)
lock.release()

综上所述,Python多线程编程中的锁机制就是这样的。希望可以给大家带来帮助。

原创文章,作者:小编小本本,如若转载,请注明出处:https://www.benjiyun.com/yunzhujiyunwei/vps-yunwei/7138.html