热门标签 | HotTags
当前位置:  开发笔记 > 编程语言 > 正文

关于多线程:Python

PythonThreads-CriticalSection什么是线程的临界区(在Python中)?Athreadentersthecriticalsectionbycallin

文章目录[隐藏]

  • 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

另外,锁的用途是什么?


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

1
2
3
4
5
6
7
8
9
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 术语绑定它)

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

1
2
3
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 的更新是"线程安全的"(并行安全)。

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

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

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
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" 锁只是意味着"我得到了房间。"

  • -1 用于传播一个非常错误和糟糕的设计选择:丑陋的每个连接一个线程的方法很常见但错误。
  • 试着告诉 Erlang 的人。在许多编程语言中它可能是错误的,但由于它如此普遍(并提供了如此有用的示例),我决定使用它。在关于连接池的问题中,我会说别的:)


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

  • 新手可能会对您的建议感到困惑。听起来您在说可以从多个线程共享的内存中读取而无需锁定,当然不是(除非您确定写入是原子的)。
  • 我不确定我们是否存在分歧。只要写入是原子的,读取就很好。每个人都可以阅读 const 而无需关键部分。
  • 是的,但是如果您正在写入数据数组、列表或字典,而另一个线程正在读取它,则可能会在中间状态下读取它,除非读取也是"原子",使用相同的信号量/锁。
  • 当然,这是对数组等的访问不是原子的事实的结果。



推荐阅读
  • 计算机存储系统的层次结构及其优势
    本文介绍了计算机存储系统的层次结构,包括高速缓存、主存储器和辅助存储器三个层次。通过分层存储数据可以提高程序的执行效率。计算机存储系统的层次结构将各种不同存储容量、存取速度和价格的存储器有机组合成整体,形成可寻址存储空间比主存储器空间大得多的存储整体。由于辅助存储器容量大、价格低,使得整体存储系统的平均价格降低。同时,高速缓存的存取速度可以和CPU的工作速度相匹配,进一步提高程序执行效率。 ... [详细]
  • Webpack5内置处理图片资源的配置方法
    本文介绍了在Webpack5中处理图片资源的配置方法。在Webpack4中,我们需要使用file-loader和url-loader来处理图片资源,但是在Webpack5中,这两个Loader的功能已经被内置到Webpack中,我们只需要简单配置即可实现图片资源的处理。本文还介绍了一些常用的配置方法,如匹配不同类型的图片文件、设置输出路径等。通过本文的学习,读者可以快速掌握Webpack5处理图片资源的方法。 ... [详细]
  • 本文讨论了clone的fork与pthread_create创建线程的不同之处。进程是一个指令执行流及其执行环境,其执行环境是一个系统资源的集合。在调用系统调用fork创建一个进程时,子进程只是完全复制父进程的资源,这样得到的子进程独立于父进程,具有良好的并发性。但是二者之间的通讯需要通过专门的通讯机制,另外通过fork创建子进程系统开销很大。因此,在某些情况下,使用clone或pthread_create创建线程可能更加高效。 ... [详细]
  • 本文介绍了操作系统的定义和功能,包括操作系统的本质、用户界面以及系统调用的分类。同时还介绍了进程和线程的区别,包括进程和线程的定义和作用。 ... [详细]
  • 本文介绍了解决Netty拆包粘包问题的一种方法——使用特殊结束符。在通讯过程中,客户端和服务器协商定义一个特殊的分隔符号,只要没有发送分隔符号,就代表一条数据没有结束。文章还提供了服务端的示例代码。 ... [详细]
  • Linux服务器密码过期策略、登录次数限制、私钥登录等配置方法
    本文介绍了在Linux服务器上进行密码过期策略、登录次数限制、私钥登录等配置的方法。通过修改配置文件中的参数,可以设置密码的有效期、最小间隔时间、最小长度,并在密码过期前进行提示。同时还介绍了如何进行公钥登录和修改默认账户用户名的操作。详细步骤和注意事项可参考本文内容。 ... [详细]
  • Iamtryingtomakeaclassthatwillreadatextfileofnamesintoanarray,thenreturnthatarra ... [详细]
  • 本文介绍了在rhel5.5操作系统下搭建网关+LAMP+postfix+dhcp的步骤和配置方法。通过配置dhcp自动分配ip、实现外网访问公司网站、内网收发邮件、内网上网以及SNAT转换等功能。详细介绍了安装dhcp和配置相关文件的步骤,并提供了相关的命令和配置示例。 ... [详细]
  • Linux重启网络命令实例及关机和重启示例教程
    本文介绍了Linux系统中重启网络命令的实例,以及使用不同方式关机和重启系统的示例教程。包括使用图形界面和控制台访问系统的方法,以及使用shutdown命令进行系统关机和重启的句法和用法。 ... [详细]
  • 本文介绍了P1651题目的描述和要求,以及计算能搭建的塔的最大高度的方法。通过动态规划和状压技术,将问题转化为求解差值的问题,并定义了相应的状态。最终得出了计算最大高度的解法。 ... [详细]
  • 判断数组是否全为0_连续子数组的最大和的解题思路及代码方法一_动态规划
    本文介绍了判断数组是否全为0以及求解连续子数组的最大和的解题思路及代码方法一,即动态规划。通过动态规划的方法,可以找出连续子数组的最大和,具体思路是尽量选择正数的部分,遇到负数则不选择进去,遇到正数则保留并继续考察。本文给出了状态定义和状态转移方程,并提供了具体的代码实现。 ... [详细]
  • 在重复造轮子的情况下用ProxyServlet反向代理来减少工作量
    像不少公司内部不同团队都会自己研发自己工具产品,当各个产品逐渐成熟,到达了一定的发展瓶颈,同时每个产品都有着自己的入口,用户 ... [详细]
  • Linux环境变量函数getenv、putenv、setenv和unsetenv详解
    本文详细解释了Linux中的环境变量函数getenv、putenv、setenv和unsetenv的用法和功能。通过使用这些函数,可以获取、设置和删除环境变量的值。同时给出了相应的函数原型、参数说明和返回值。通过示例代码演示了如何使用getenv函数获取环境变量的值,并打印出来。 ... [详细]
  • 前景:当UI一个查询条件为多项选择,或录入多个条件的时候,比如查询所有名称里面包含以下动态条件,需要模糊查询里面每一项时比如是这样一个数组条件:newstring[]{兴业银行, ... [详细]
  • Java学习笔记之面向对象编程(OOP)
    本文介绍了Java学习笔记中的面向对象编程(OOP)内容,包括OOP的三大特性(封装、继承、多态)和五大原则(单一职责原则、开放封闭原则、里式替换原则、依赖倒置原则)。通过学习OOP,可以提高代码复用性、拓展性和安全性。 ... [详细]
author-avatar
看破红尘红尘看破_728
这个家伙很懒,什么也没留下!
PHP1.CN | 中国最专业的PHP中文社区 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved | 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有