首页 > 编程问答 >python threading.Condition 的意外行为

python threading.Condition 的意外行为

时间:2024-07-23 05:15:10浏览次数:17  
标签:python multithreading synchronization infinite-loop

我正在尝试同步多个线程。我期望使用 threading.Condition 和 threading.Barrier 时的脚本输出大致相同,但事实并非如此。请解释一下为什么会发生这种情况。

一般来说,我需要线程在一个 无限循环中执行工作(一些IO操作) ,但是每个循环都是以主线程的权限开始的,而权限是仅在所有线程完成前一个周期后给出。

脚本 1

from threading import Barrier, Thread
from time import sleep, time

br = Barrier(3)
store = []

def f1():
    while True:
        br.wait()
        sleep(1)
        print("Calc part1")

def f2():
    while True:
        br.wait()
        sleep(1)
        print("Calc part2")


Thread(target=f1).start()
Thread(target=f2).start()

for i in range(10):
    br.wait()
    print(f'end iter {i}')
    print(f'-------------')

预期行为

ent iter 0
-------------
Calc part1
Calc part2
ent iter 1
-------------
Calc part2
Calc part1
ent iter 2
-------------
Calc part1
...

脚本 2

from threading import Condition, Thread
from time import sleep

condition = False
cv = Condition()

def predicate():
    return condition

def f1():
    for i in range(3):
        with cv:
            cv.wait_for(predicate)
            sleep(1)
            print("Calc part1")

def f2():
    for i in range(3):
        with cv:
            cv.wait_for(predicate)
            sleep(1)
            print("Calc part2")


Thread(target=f1).start()
Thread(target=f2).start()

with cv:
    condition = True
    cv.notify_all()

意外行为

Calc part1
Calc part1
Calc part1
Calc part2
Calc part2
Calc part2

为什么线程的标准输出不混合在第二个脚本的结果中?


第二个脚本中的线程输出没有像预期的那样混合,因为 cv.notify_all() 不会在 with cv: 块结束之前实际释放锁 。这意味着第一个获得 CPU 时间片的线程(在的情况下似乎是 f1 )将在释放锁并允许 f2 继续运行之前完成其所有 3 次迭代。

以下是更详细的解释:

  1. 获取锁: f1 f2 到达 with cv: 块时,它们都会尝试获取 cv 的底层锁。只有一个线程可以一次获取锁。
  2. 等待条件: 获取锁的线程会调用 cv.wait_for(predicate) 。由于 condition 最初设置为 False ,因此两个线程都会在锁释放的情况下被阻塞并等待。
  3. 主线程通知: 主线程进入其 with cv: 块,获取锁,设置 condition = True ,并调用 cv.notify_all() 。这将唤醒所有正在等待的线程( f1 f2 )。
  4. 竞争锁: f1 f2 现在都在竞争获取锁。但是, cv.notify_all() 不会立即释放锁。 它只是标记线程在下次获取锁时可以继续运行。主线程仍然持有锁。
  5. f1 继续运行: 在的情况下, f1 首先获取锁(这取决于线程调度)。由于 condition 现在是 True ,因此 cv.wait_for(predicate) 立即返回, f1 执行其循环中的三轮操作,打印三次 “Calc part1”。重要的是, 在整个过程中, f1 一直持有锁
  6. f2 获取锁: 只有在 f1 释放锁(通过退出其 with cv: 块)后, f2 才最终有机会获取锁并运行。然后它也执行其三轮操作,打印三次 “Calc part2”。

要实现类似于 Barrier 的行为,需要在每次迭代后通知线程并使用循环来处理潜在的虚假唤醒。

以下是修改后的代码:

from threading import Condition, Thread
from time import sleep

condition = False
cv = Condition()

def predicate():
    return condition

def f1():
    for i in range(3):
        with cv:
            while not predicate():  # 使用循环处理虚假唤醒
                cv.wait()
            sleep(1)
            print("Calc part1")
            cv.notify_all() # 通知其他线程

def f2():
    for i in range(3):
        with cv:
            while not predicate():  # 使用循环处理虚假唤醒
                cv.wait()
            sleep(1)
            print("Calc part2")
            cv.notify_all() # 通知其他线程

Thread(target=f1).start()
Thread(target=f2).start()

for i in range(3):
    with cv:
        condition = True
        cv.notify_all()
        condition = False # 重置条件以便下次迭代

在这个版本中,每个线程在完成其工作后都会通知其他线程,并且循环确保只有在满足条件时才会继续运行。主线程负责在每次迭代后重置条件。

请注意,尽管此修改后的代码可以实现类似于 Barrier 的行为,但在实际应用程序中使用 Barrier 通常更简单、更不容易出错。

标签:python,multithreading,synchronization,infinite-loop
From: 78780758

相关文章

  • Python - 逆透视数据框
    我有一个按日期时间索引的表,每个日期时间都有多个层(中心和交货间隔):日期时间中心交货间隔结算点价格2024-01-0101:00:00休斯顿中心1......
  • 试图理解这个错误:致命的Python错误:PyEval_RestoreThread:该函数必须在持有GIL的情况下
    我有一个小型tkinter应用程序,我一直在其中实现最小的“拖放”,主要作为学习实验。我真正关心的是删除文件的文件路径。一切实际上都工作正常,直到我尝试在拖放后打包标签小部件。下面的最小工作示例。有问题的行会用注释指出。我通常不会在调试方面遇到太多麻烦,但我只是不知......
  • 如何使代码格式再次适用于 Python(Mac 上的 Visual Studio Code)?
    在Mac上,Option+Shift+F现在会显示“没有安装用于‘python’文件的格式化程序”。消息框:我尝试安装这个插件,但没有看到这种情况的变化:我已经为Python安装了这两个插件:但是正如@starball提到的,它可能已经减少了支持现在。......
  • 无法在 python 中安装 pip install expliot - bluepy 的 Building Wheel (pyproject.t
    在此处输入图像描述当我尝试在Windows计算机中通过cmd安装pipinstallexpliot包时,我收到2个错误名称×Buildingwheelforbluepy(pyproject.toml)didnotrunsuccessfully.│exitcode:1**AND**opt=self.warn_dash_deprecation......
  • python 用单斜杠-反斜杠替换url字符串中的双斜杠
    我的URL包含错误的双斜杠(“//”),我需要将其转换为单斜杠。不用说,我想保持“https:”后面的双斜杠不变。可以在字符串中进行此更改的最短Python代码是什么?我一直在尝试使用re.sub,带有冒号否定的正则表达式(即,[^:](//)),但它想要替换整个匹配项(包括前面......
  • 如何使用 Selenium Python 搜索 Excel 文件中的文本
    我有一些数据在Excel文件中。我想要转到Excel文件,然后搜索文本(取自网站表),然后获取该行的所有数据,这些数据将用于在浏览器中填充表格。示例:我希望selenium搜索ST0003然后获取名称,该学生ID的父亲姓名,以便我可以在大学网站中填写此信息。我想我会从网站......
  • Python 套接字请求在很多情况下都会失败
    我在python中尝试了超过5种不同的方法,尽管人们说它在其他论坛上有效,但所有这些方法都惨遭失败。importsocketmessage="test"clientsocket=socket.socket(socket.AF_INET,socket.SOCK_STREAM)clientsocket.connect(('1.1.1.1',80))clientsocket.send(mes......
  • Python 网络套接字
    我一直尝试通过Python访问该网站的websocket,但是需要绕过CloudFlare,现在我尝试通过cookie进行绕过,但是这不起作用。我已经尝试在没有cookie的情况下执行此操作,但这也不起作用。importwebsocketimportbase64importosdriver=selenium.webdriver.Firefox()driver.ge......
  • 如何在Python中使用Selenium提取data-v-xxx?
    因为我想查看每个class='num'内的文本是否大于0。如果测试通过,那么我需要获取venuen-name内的文本。我观察到,data-v是相同的。所以我的方法是获取相同的data-v-<hashvalue>来查找场地名称。我尝试了不同的方法来提取,但仍然无法提取。有什么建议吗?这是DOM<div......
  • Python:添加异常上下文
    假设我想提出一个异常并提供额外的处理信息;最好的做法是什么?我想出了以下方法,但对我来说有点可疑:definternal_function():raiseValueError("smellysocks!")defcontext_function():try:internal_function()exceptExceptionase:......