Python线程 - 关键部分

时间:2009-01-07 03:58:18

标签: python multithreading language-agnostic

线程的“关键部分”是什么(在Python中)?

  

线程进入临界区   通过调用acquire()方法,其中   可以阻止或   无阻塞。一个线程退出   关键部分,通过调用   release()方法。

- Understanding Threading in Python, Linux Gazette

另外,锁的目的是什么?

3 个答案:

答案 0 :(得分:16)

代码的关键部分是一次只能由一个线程执行的代码。以聊天服务器为例。如果每个连接都有一个线程(即每个最终用户),则一个“关键部分”是假脱机代码(向所有客户端发送传入消息)。如果多个线程一次尝试假脱机,你会得到BfrIToS mttwD PIoEmesCEsaSges交织在一起,这显然是不好的。

锁可以用于同步对关键部分(或一般资源)的访问。在我们的聊天服务器示例中,锁就像一个带有打字机的锁定房间。如果一个线程在那里(键入消息),没有其他线程可以进入房间。第一个线程完成后,他解锁房间并离开。然后另一个线程可以进入房间(锁定它)。 “获取”锁只意味着“我得到了房间。”

答案 1 :(得分:16)

其他人给出了非常好的定义。这是典型的例子:

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

假设+=运算符由三个子组件组成:

  • 读取当前值
  • 将RHS添加到该值
  • 将累计值写回LHS(技术上用Python术语 bind

如果您没有with account_balance_lock语句并且并行执行两个change_account_balance调用,则最终会以危险方式交错三个子组件操作。假设您同时调用change_account_balance(100)(AKA pos)和change_account_balance(-100)(AKA neg)。这可能发生:

pos = threading.Thread(target=change_account_balance, args=[100])
neg = threading.Thread(target=change_account_balance, args=[-100])
pos.start(), neg.start()
  • pos:读取当前值 - > 0
  • neg:读取当前值 - > 0
  • pos:将当前值添加到读取值 - > 100
  • neg:将当前值添加到读取值 - > -100
  • pos:写入当前值 - > account_balance = 100
  • neg:写入当前值 - > account_balance = -100

因为您没有强制操作以离散块的形式发生,所以可以有三种可能的结果(-100,0,100)。

with [lock]语句是一个不可分割的单一操作,它说:“让我成为执行这段代码的唯一线程。如果还有其他东西正在执行,那很酷 - 我会等。”这可确保account_balance的更新是“线程安全的”(并行安全)。

注意:此架构有一个警告:每次要操纵{{1}时,您必须记住获取account_balance_lock(通过with)代码保持线程安全。有一些方法可以使这不那么脆弱,但这是另一个问题的答案。

编辑:回想起来,提及account_balance语句隐含地在锁上调用阻塞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() 的不可分割性),这是一个简单的布尔标志无法保证的。原子操作的语义可能也是另一个问题的内容。

答案 2 :(得分:0)

"critical section"是一段代码,其中,为了正确起见,有必要确保一次只有一个控制线程可以在该部分中。通常,您需要一个关键部分来包含值写入内存的引用,这些引用可以在多个并发进程之间共享。