什么是线程(Python中)的“临界区”?
线程通过调用 acquire() 方法进入临界区,该方法可以是阻塞或非阻塞的。线程通过调用 release() 方法退出临界区。
- 了解 Python 中的线程,Linux Gazette
此外,锁的目的是什么?
什么是线程(Python中)的“临界区”?
线程通过调用 acquire() 方法进入临界区,该方法可以是阻塞或非阻塞的。线程通过调用 release() 方法退出临界区。
- 了解 Python 中的线程,Linux Gazette
此外,锁的目的是什么?
其他人已经给出了非常好的定义。这里是一个经典的例子:
import threading
account_balance = 0 # The "resource" that zenazn mentions.
account_balance_lock = threading.Lock()
def change_account_balance(delta):
global account_balance
with account_balance_lock:
# Critical section is within this block.
account_balance += delta
+=
操作符由三个子组件组成:
with account_balance_lock
语句并且同时执行两个 change_account_balance
调用,则可能以危险的方式交错三个子组件操作。 假设您同时调用 change_account_balance(100)
(也称为pos)和change_account_balance(-100)
(也称为neg)。 这可能会发生:pos = threading.Thread(target=change_account_balance, args=[100])
neg = threading.Thread(target=change_account_balance, args=[-100])
pos.start(), neg.start()
由于您没有强制操作以离散块的形式进行,因此可能会有三种可能的结果(-100、0、100)。
with [lock]
语句是一种单独不可分割的操作,它表示:“让我是唯一执行此代码块的线程。如果其他线程正在执行,则没问题——我会等待。”这确保了对account_balance
的更新是“线程安全的”(并行安全的)。
注意:这种模式有一个警告:您必须记住每次想要操作account_balance
时通过with
获取account_balance_lock
,以使代码保持线程安全。有办法使其变得不那么脆弱,但那是另一个问题的答案。
编辑:回想起来,可能重要的是提到with
语句隐式调用锁定的阻塞acquire
- 这是上面线程对话框中的“我会等待”部分。相比之下,非阻塞获取说:“如果我不能立即获得锁,请告诉我”,然后依赖于您检查是否已经获得了锁。
import logging # This module is thread safe.
import threading
LOCK = threading.Lock()
def run():
if LOCK.acquire(False): # Non-blocking -- return whether we got it
logging.info('Got the lock!')
LOCK.release()
else:
logging.info("Couldn't get the lock. Maybe next time")
logging.basicConfig(level=logging.INFO)
threads = [threading.Thread(target=run) for i in range(100)]
for thread in threads:
thread.start()
acquire
),而简单的布尔标志无法保证。原子操作的语义可能也是另一个问题的内容。代码的关键部分是只能由一个线程执行的部分。以聊天服务器为例,如果您为每个连接(即每个终端用户)创建一个线程,则“关键部分”是Spooling代码(将传入消息发送给所有客户端)。如果多个线程同时尝试排队一条消息,则会得到交织在一起的BfrIToS mANtwD PIoEmesCEsaSges,这显然是不好的。
锁是可用于同步访问关键部分(或资源总体)的东西。在我们的聊天服务器示例中,锁就像带有打字机的锁定房间。如果有一个线程在里面(以便将一条消息打出来),则其他线程无法进入该房间。一旦第一个线程完成,他解锁该房间并离开。然后,另一个线程可以进入该房间(锁定它)。“获取”锁只意味着“我得到了房间”。
"关键区段"是一段代码,为了正确性,必须确保在同一时间只有一个控制线程可以进入该区段。通常,您需要一个关键区段来包含引用,这些引用将值写入到可以在多个并发进程之间共享的内存中。