Python|如何锁定关键部分
本文旨在如何锁定给定程序中的线程和临界区以避免竞争条件。因此,在线程库中使用 Lock 对象使可变对象可以安全地被多个线程使用。
代码#1:
import threading
class counter_share:
'''
multiple threads can share.
'''
def __init__(self, initial_key = 0):
self._key = initial_key
self._key_lock = threading.Lock()
def incr(self, delta = 1):
with self._key_lock:
# Increasing the counter with lock
self._key += delta
def decr(self, delta = 1):
with self._key_lock:
# Decreasing the counter with lock
self._key -= delta
将with语句与锁一起使用可确保互斥。排除是指一次只允许一个线程(在 with 语句下)执行语句块。
当控制流退出缩进块时,获取并释放预期语句期间的锁。线程调度本质上是不确定的。因此,由于使用锁失败,可能会导致随机损坏的数据和“竞争条件”。因此,每当多个线程访问共享的可变状态时,应始终使用锁来避免这种情况。
在较旧的Python代码中,通常会看到显式获取和释放锁。
代码 #2:代码 1 的变体
import threading
class counter_share:
# multiple threads can share counter objects
def __init__(self, initial_key = 0):
self._key = initial_key
self._key_lock = threading.Lock()
def incr(self, delta = 1):
# Increasing the counter with lock
self._key_lock.acquire()
self._key += delta
self._key_lock.release()
def decr(self, delta = 1):
# Decreasing the counter with lock
self._key_lock.acquire()
self._key -= delta
self._key_lock.release()
- 在没有调用
release()
方法或持有锁时没有引发异常的情况下, with语句不太容易出错。 - 不允许程序中的每个线程一次获取一个锁,这样可以潜在地避免死锁的情况。如果不可能,请在程序中引入更高级的死锁避免。
- 在线程库中可以找到同步原语,例如 RLock 和 Semaphore 对象。
除了简单的模块锁定,还有一些更特殊的目的被解决:
- RLock 或可重入锁对象是可以由同一线程多次获取的锁。
- 它主要基于锁定或同步一个称为“监视器”的结构来实现代码。在持有锁时,只允许一个线程使用整个函数或类的方法,这种类型的锁定。
代码 #3:实现 SharedCounter 类。
import threading
class counter_share:
# multiple threads can share counter objects
_lock = threading.RLock()
def __init__(self, initial_key = 0):
self._key = initial_key
def incr(self, delta = 1):
# Increasing the counter with lock
with SharedCounter._lock:
self._key += delta
def decr(self, delta = 1):
# Decreasing the counter with lock
with SharedCounter._lock:
self.incr(-delta)
- 锁旨在同步类的方法,尽管锁与每个实例的可变状态相关联。
- 在此代码变体中,类的所有实例只共享一个类级别的锁。
- 确保一次只允许一个线程使用该类的方法。
- 如果方法已经拥有锁,则可以调用其他也使用锁的方法。 (例如
decr()
方法)。 - 如果有大量计数器,则内存效率更高。但是,它可能会在使用大量线程并进行频繁计数器更新的程序中导致更多的锁争用。
信号量项是依赖于相互计数器的同步粗略。计数器在正方形结束时增加。如果计数器为零,则会阻碍前进,直到计数器增加另一个字符串为止。如果计数器不为零,则说明会减少计数并允许字符串继续。
与直接锁定不同,信号量项对于包括字符串之间的运动或节流在内的应用程序越来越有价值。尽管信号量可以以与标准锁类似的方式使用,但使用中额外的多方面性质反而会影响执行。
代码#4:要限制部分代码中的并发量,请使用信号量。
from threading import Semaphore
import urllib.request
# five threads are allowed to run at once (at max)
_fetch_url_sema = Semaphore(5)
def fetch_url(url):
with _fetch_url_sema:
return urllib.request.urlopen(url)