Python Threads – Critical Section

Python Threads - Critical Section

什么是线程的"临界区"(在 Python 中)?

A thread enters the critical section

by calling the acquire() method, which

can either be blocking or

non-blocking. A thread exits the

critical section, by calling the

release() method.

- 理解 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 += deltapos = threading.Thread(target=change_account_balance, args=[100])

neg = threading.Thread(target=change_account_balance, args=[-100])

pos.start(), neg.start()

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()

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

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

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

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 += deltapos = threading.Thread(target=change_account_balance, args=[100])

neg = threading.Thread(target=change_account_balance, args=[-100])

pos.start(), neg.start()

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()
  • pos: 读取当前值 -> 0
  • neg: 读取当前值 -> 0
  • pos:将当前值添加到读取值 -> 100
  • neg:将当前值添加到读取值 -> -100
  • pos: 写入当前值 -> account_balance = 100
  • neg: 写入当前值 -> account_balance = -100

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

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

注意:此模式有一个警告:您必须记住每次要操作 account_balance 时都获取 account_balance_lock(通过 with)以使代码保持线程安全。有一些方法可以减少这种脆弱性,但这是另一个问题的答案。

编辑:回想起来,可能很重要的是要提到 with 语句隐式调用锁上的阻塞 acquire ——这是上面的"我将等待"部分线程对话框。相反,非阻塞获取会说,"如果我不能立即获取锁,请告诉我",然后依靠您检查是否获得了锁。

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 += deltapos = threading.Thread(target=change_account_balance, args=[100])

neg = threading.Thread(target=change_account_balance, args=[-100])

pos.start(), neg.start()

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 跨线程的不可分割性),一个简单的布尔标志不能保证。原子操作的语义大概也是另一个问题的内容。


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

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


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


相关推荐

  • Spring部署设置openshift

    Springdeploymentsettingsopenshift我有一个问题让我抓狂了三天。我根据OpenShift帐户上的教程部署了spring-eap6-quickstart代码。我已配置调试选项,并且已将Eclipse工作区与OpehShift服务器同步-服务器上的一切工作正常,但在Eclipse中出现无法消除的错误。我有这个错误:cvc-complex-type.2.4.a:Invali…
    2025-04-161
  • 检查Java中正则表达式中模式的第n次出现

    CheckfornthoccurrenceofpatterninregularexpressioninJava本问题已经有最佳答案,请猛点这里访问。我想使用Java正则表达式检查输入字符串中特定模式的第n次出现。你能建议怎么做吗?这应该可以工作:MatchResultfindNthOccurance(intn,Patternp,CharSequencesrc){Matcherm=p.matcher…
    2025-04-161
  • 如何让 JTable 停留在已编辑的单元格上

    HowtohaveJTablestayingontheeditedcell如果有人编辑JTable的单元格内容并按Enter,则内容会被修改并且表格选择会移动到下一行。是否可以禁止JTable在单元格编辑后转到下一行?原因是我的程序使用ListSelectionListener在单元格选择上同步了其他一些小部件,并且我不想在编辑当前单元格后选择下一行。Enter的默认绑定是名为selectNext…
    2025-04-161
  • Weblogic 12c 部署

    Weblogic12cdeploy我正在尝试将我的应用程序从Tomcat迁移到Weblogic12.2.1.3.0。我能够毫无错误地部署应用程序,但我遇到了与持久性提供程序相关的运行时错误。这是堆栈跟踪:javax.validation.ValidationException:CalltoTraversableResolver.isReachable()threwanexceptionatorg.…
    2025-04-161
  • Resteasy Content-Type 默认值

    ResteasyContent-Typedefaults我正在使用Resteasy编写一个可以返回JSON和XML的应用程序,但可以选择默认为XML。这是我的方法:@GET@Path("/content")@Produces({MediaType.APPLICATION_XML,MediaType.APPLICATION_JSON})publicStringcontentListRequestXm…
    2025-04-161
  • 代码不会停止运行,在 Java 中

    thecodedoesn'tstoprunning,inJava我正在用Java解决项目Euler中的问题10,即"Thesumoftheprimesbelow10is2+3+5+7=17.Findthesumofalltheprimesbelowtwomillion."我的代码是packageprojecteuler_1;importjava.math.BigInteger;importjava…
    2025-04-161
  • Out of memory java heap space

    Outofmemoryjavaheapspace我正在尝试将大量文件从服务器发送到多个客户端。当我尝试发送大小为700mb的文件时,它显示了"OutOfMemoryjavaheapspace"错误。我正在使用Netbeans7.1.2版本。我还在属性中尝试了VMoption。但仍然发生同样的错误。我认为阅读整个文件存在一些问题。下面的代码最多可用于300mb。请给我一些建议。提前致谢publicc…
    2025-04-161
  • Log4j 记录到共享日志文件

    Log4jLoggingtoaSharedLogFile有没有办法将log4j日志记录事件写入也被其他应用程序写入的日志文件。其他应用程序可以是非Java应用程序。有什么缺点?锁定问题?格式化?Log4j有一个SocketAppender,它将向服务发送事件,您可以自己实现或使用与Log4j捆绑的简单实现。它还支持syslogd和Windows事件日志,这对于尝试将日志输出与来自非Java应用程序…
    2025-04-161