gpt4 book ai didi

python - 在PyQt QThreads中同步事件

转载 作者:行者123 更新时间:2023-11-28 21:56:37 25 4
gpt4 key购买 nike

我在玩PyQt和QThreads。看来,如果我使用放置在this python fiddle中的代码(请注意,顶部是从QtDesigner自动生成的代码),则该循环的当前值会在从属线程的循环和该循环中同时打印控制进度条,然后循环保持同步,程序运行时所有值都匹配,进度条准确显示已完成的从属线程的比例。

作为对以下评论的回应,该程序在当前状态下实际上可以执行我想要的操作-它只是在终端上打印出从属线程中循环的值以及控制进度的循环中的值进度条。

但是,注释掉第121行(即,如果您不在进度条循环中打印当前值),则当从属线程循环仅达到〜130次迭代时,进度条达到100%(即完成300次迭代)(即进度条的完成速度大约快100%)。

我是否做过一些天真的愚蠢/错误的事情-是否有更好的方法来完成我想做的事情?

最佳答案

仅仅因为您有两个循环在不同的线程中执行相同的迭代次数,并不意味着它们将花费相同的时间长度来完成。通常,每次迭代的内容将花费一些时间来完成,并且由于您的循环在做不同的事情,因此它们(通常)将花费不同的时间长度。

此外,借助Python中的线程,全局解释器锁(GIL)阻止了线程在多核CPU上同时运行。 GIL负责在线程之间进行切换,并继续执行,然后再切换到另一个,然后再切换到另一个,依此类推。对于QThreads,这变得更加复杂,因为QThread中的Qt调用可以在没有GIL的情况下运行(所以我了解),但一般的python代码仍将与GIL一起运行。

因为GIL负责处理在任何给定时间正在运行的线程,所以我什至看到两个做相同的事情的线程以不同的速度运行。 这样,您的两个线程同时完成完全是巧合!

请注意,由于使用了GIL,两个cpu密集型任务无法在单独的线程中运行而带来速度上的好处。为此,您需要使用多重处理。但是,如果您要输出受I/O约束的任务(例如,通过主线程中的GUI进行用户界面操作,或通过另一个线程中的网络通信,也就是那些通常花费大量时间等待程序外部内容的任务)的任务,触发某些东西),那么线程很有用。

因此,希望这有助于解释线程以及程序中发生的事情。

有两种方法可以更好地做到这一点。一种方法是将循环保留在您的线程中,而另一种则删除。然后使用qt signal/slot机制调用MainWindow中的一个函数,该函数运行曾经在那里的循环的一次迭代。但是,这不能保证同步,只是您的QThread将首先完成(某些操作可能会减慢主线程的速度,从而使事件堆积,并且MainWindow中的函数要等到以后才能运行)。要完成同步,可以使用threading.Event对象使QThread等待MainWindow中的新函数运行。

示例(未经测试,很抱歉,但希望能给出这个主意!):

import threading
#==========================================
class TaskThread(QtCore.QThread):

setTime = QtCore.pyqtSignal(int,int)
iteration = QtCore.pyqtSignal(threading.Event, int)

def run(self):

self.setTime.emit(0,300)
for i in range(300):
time.sleep(0.05)
event = threading.Event()
self.iteration.emit(event, i)
event.wait()

#==========================================
class MainWindow(QtGui.QMainWindow):

_uiform = None

def __init__(self, parent=None):
QtGui.QMainWindow.__init__(self,parent)
self._uiform = Ui_MainWindow()
self._uiform.setupUi(self)
self._uiform.runButton.clicked.connect(self.startThread)


def startThread(self):
self._uiform.progressBar.setRange(0,0)
self.task = TaskThread()
self.task.setTime.connect(self.changePB)
self.task.iteration.connect(self.update_prog_bar)
self.task.start()

@QtCore.pyqtSlot(int,int)
def changePB(self, c, t):
self.proportionFinished = int(math.floor(100*(float(c)/t)))
self._uiform.progressBar.setValue(self.proportionFinished)

self._uiform.progressBar.setRange(0,300)
self._uiform.progressBar.setValue(0)

@QtCore.pyqtSlot(threading._Event,int)
def update_prog_bar(self,event, i)
self._uiform.progressBar.setValue(i+1)
print i
event.set()

请注意,使用 @QtCore.pyqtSlot()装饰器是因为记录了 here的问题。简而言之,当您使用 signal.connect(my_function)时,您将忽略确定插槽行为的第二个参数(是否在调用 signal.emit()时立即执行,还是是否在控件返回事件循环后立即执行(又称为,队列以待稍后运行))。默认情况下,connect的这个可选参数会尝试自动确定通常可以进行哪种连接(请参阅 here)。但是,如果在知道连接是线程之间的连接之前进行了连接,并且使用 @pyqtSlot将“slot”明确定义为“slot”,则pyQT会感到困惑!

有关修饰符的附加信息:想到修饰符的最简单方法是将一个函数包装在另一个函数中的简写形式。装饰器将自己定义的函数替换为自己的函数,并且此新函数通常会在某个时候使用您的原始函数。因此,在 @pyqtSlot情况下,信号发射实际上调用了 @pyqtSlot生成的pyqt函数,该函数最终调用了您编写的原始函数。 @pyqtSlot装饰器采用与插槽的参数类型相匹配的参数的事实是pyqt装饰器的实现细节,通常不代表装饰器。仅说明您的插槽希望通过连接的信号传递指定类型的数据。

关于python - 在PyQt QThreads中同步事件,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/21004298/

25 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com