Qt优雅地结束线程(两种方法都是用Mutex锁住bool变量进行修改,然后由bool变量控制耗时动作的退出,即正常退出)

日期: 2019-12-06 15:22 浏览次数 :

第一来看一个例证:

小说首发个人博客:http://zmister.com

意气风发经四个线程运行完结,就能结束。可超多状态并不是这么轻便,由于某种特殊原因,当线程尚未实践完时,大家就想中止它。
不对劲的暂停往往会引起局部不解错误。举例:当关闭主分界面包车型地铁时候,很有希望次线程正在运营,那时候,就能现身如下提醒:
QThread: Destroyed while thread is still running
那是因为次线程还在运作,就结束了UI主线程,引致事件循环甘休。那一个主题素材在使用线程的历程中一时境遇,特别是耗费时间操作。
在那主题材料上,家常便饭的二种人:
1.直接忽视此难题。
2.强制中止 - terminate(卡塔尔。
许多状态下,当程序退出时,次线程恐怕会健康退出。那个时候,固然抱着侥幸心思,但祸患依然留存,只怕在极少数动静下,就能够现身Crash。
正如前方提到过terminate(卡塔尔(英语:State of Qatar),比较危殆,不慰勉接纳。线程能够在代码实施的此外点被结束。线程可能在立异数据时被终止,进而没有时机来清理本人,解锁等等。。。总体上看,独有在相对少不了时行使此函数。
之所以,我们理应利用合理的不二诀要来崇高地结束线程,日常思路:
1.号令线程退出操作,调用quit(卡塔尔(قطر‎或exit(卡塔尔。
2.等候线程完全终止,删除创制在堆上的指标。
3.适宜的使用wait(卡塔尔国(用于等待线程的淡出)和客体的算法。
上面介绍两种方法:
风度翩翩.QMutex互斥锁 + bool成员变量。
这种艺术是Qt4.x中比较常用的,主借使应用“QMutex互斥锁 + bool成员变量”的艺术来保管分享数据的安全性(能够完全参照上面包车型客车requestInterruption(卡塔尔(英语:State of Qatar)源码写法)。

# coding=utf-8 
__author__ = 'a359680405'  
from PyQt5.QtCore import * 
from PyQt5.QtGui import * 
from PyQt5.QtWidgets import *  
global sec 
sec=0 
def setTime(): 
  global sec 
  sec+=1 
  lcdNumber.display(sec)     #LED显示数字+1  
def work(): 
  timer.start(1000)        #计时器每秒计数 
  for i in range(2000000000): 
    pass 
  timer.stop()  
app=QApplication([]) 
top=QWidget() 
layout=QVBoxLayout(top)       #垂直布局类QVBoxLayout; 
lcdNumber=QLCDNumber()       #加个显示屏 
layout.addWidget(lcdNumber) 
button=QPushButton("测试") 
layout.addWidget(button)  
timer=QTimer() 
timer.timeout.connect(setTime)   #每次计时结束,触发setTime 
button.clicked.connect(work)  
top.show() 
app.exec() 

Python GUI教程(大器晚成):在PyQt第55中学创制第4个GUI图形客户分界面
Python GUI教程(二):增加窗口小零器件到图形客户界面GUI中
Python GUI教程(三):在GUI窗口中开展结构管理
Python GUI教程(四):安装并选用Qt设计员设计Python GUI
Python GUI教程(五):通过Qt设计员在GUI中增多窗口零件
Python GUI教程(六):使用Qt设计员举办窗口布局
Python GUI教程(七):转变qt设计员的ui代码为Python代码
Python GUI教程(八):在主窗口中调用对话框
Python GUI教程(九):从UI文件中解耦Python代码
Python GUI教程(十):创制三个错落有致的GUI
Python GUI教程(十生龙活虎):使用四线程保持GUI的响应
Python GUI教程(十七):使用拖放控件
Qt优雅地结束线程(两种方法都是用Mutex锁住bool变量进行修改,然后由bool变量控制耗时动作的退出,即正常退出)。Python GUI教程(十三):在GUI中使用pyqtgraph绘图库

[cpp] view plain copy

咱俩的主分界面有多个用来呈现时间的 LCD 数字面板还也是有贰个用以运营职分的开关。程序的目标是客户点击按键,开始三个极其耗费时间的演算(程序中大家以一个2001000000 次的循环来代替这一个充足耗费时间的办事,在实际的顺序中,那恐怕是二个网络访谈,大概是内需复制叁个非常大的公文也许此外职责),同期LCD 发轫展现逝去的皮秒数。皮秒数通过八个放大计时器QTimer实行翻新。总括完结后,电磁打点计时器结束。这是叁个异常粗略的行使,也看不出有任何难题。可是当大家最早运路程序时,难题就来了:点击开关之后,程序分界面直接结束响应,直到循环停止才伊始再次更新,于是计时器使用展现0。

本篇将会涉及:

 

有经验的开拓者立刻提出,这里须求运用线程。那是因为 Qt 中有着分界面都以在 UI 线程中(也被称得上主线程,正是实行了QApplication::exec(卡塔尔国的线程),在这里个线程中实施耗费时间的操作(比方特别循环),就能够堵塞UI 线程,从而让分界面停止响应。分界面截至响应,顾客体验自然不佳,但是更要紧的是,有些窗口管理程序会检查评定到你的前后相继已经失却响应,可能会建议客户强制结束程序,那样一来你的次序只怕就此下马,职务再也心余力绌到位。所以,为了制止这一难题,大家要采取QThread 开启三个新的线程:

  • 维持GUI程序响应
  • sleep(卡塔尔冻结一切GUI程序
  • 开班、甘休和重新初始化大家的导航条
  • 使用四线程和pyqt非确定性信号

 图片 1图片 2

# coding=utf-8 
__author__ = 'a359680405'  
from PyQt5.QtCore import * 
from PyQt5.QtGui import * 
from PyQt5.QtWidgets import *  
global sec 
sec=0 
class WorkThread(QThread): 
  trigger = pyqtSignal() 
  def __int__(self): 
    super(WorkThread,self).__init__() 

  def run(self): 
    for i in range(203300030): 
      pass 
    self.trigger.emit()     #循环完毕后发出信号 

def countTime(): 
  global sec 
  sec+=1 
  lcdNumber.display(sec)     #LED显示数字+1 

def work(): 
  timer.start(1000)        #计时器每秒计数 
  workThread.start()       #计时开始 
  workThread.trigger.connect(timeStop)  #当获得循环完毕的信号时,停止计数 

def timeStop(): 
  timer.stop() 
  print("运行结束用时",lcdNumber.value()) 
  global sec 
  sec=0  
app=QApplication([]) 
top=QWidget() 
layout=QVBoxLayout(top)       #垂直布局类QVBoxLayout; 
lcdNumber=QLCDNumber()       #加个显示屏 
layout.addWidget(lcdNumber) 
button=QPushButton("测试") 
layout.addWidget(button)  
timer=QTimer() 
workThread=WorkThread() 
button.clicked.connect(work) 
timer.timeout.connect(countTime)   #每次计时结束,触发setTime  
top.show() 
app.exec() 

改革开关对速度栏的主宰

上生龙活虎篇中大家成立了三个稍显复杂的GUI,并且编写了黄金时代部分逻辑情势来决定一些开关和零件的一颦一笑。此中,我们设置了八个按键,用来支配进程条,不过意义还不是很周详。
小编们今后将其改过一下:

  • 率先个开关用来运转进程栏;
  • 其次个按键用来终止进程栏;
  • 其五个开关用来重新苏醒设置进度栏;

图片 3

在前边的MainWindow类中,咱们对进程条和开关的风云设置多个艺术:

图片 4

我们将上述多个法子改正一下,改写成多少个办法:

  • start_progressbar(self卡塔尔国:用于运维进度栏
  • stop_progressbar(self卡塔尔(قطر‎:用于甘休进度栏
  • reset_progressbar(self卡塔尔(قطر‎:用于重新苏醒设置进程栏
  • update_progressbar(self卡塔尔(قطر‎:用于绑定四个开关到上述多少个点子上

图片 5

运作程序,看看效果:

图片 6

当我们点击“此前”开关,进度栏便开头扩大,当大家点击“截止开关”,进程栏便停下增添,当大家点击“重新载入参数”按键,进程栏便归零。

  1. #include <QThread>  
  2. #include <QMutexLocker>  
  3.   
  4. class WorkerThread : public QThread  
  5. {  
  6.     Q_OBJECT  
  7.   
  8. public:  
  9.     explicit WorkerThread(QObject *parent = 0)  
  10.         : QThread(parent),  
  11.           m_bStopped(false)  
  12.     {  
  13.         qDebug() << "Worker Thread : " << QThread::currentThreadId();  
  14.     }  
  15.   
  16.     ~WorkerThread()  
  17.     {  
  18.         stop();  
  19.         quit();  
  20.         wait();  
  21.     }  
  22.   
  23.     void stop()  
  24.     {  
  25.         qDebug() << "Worker Stop Thread : " << QThread::currentThreadId();  
  26.         QMutexLocker locker(&m_mutex);  
  27.         m_bStopped = true;  
  28.     }  
  29.   
  30. protected:  
  31.     virtual void run() Q_DECL_OVERRIDE {  
  32.         qDebug() << "Worker Run Thread : " << QThread::currentThreadId();  
  33.         int nValue = 0;  
  34.         while (nValue < 100)  
  35.         {  
  36.             // 休眠50毫秒  
  37.             msleep(50);  
  38.             ++nValue;  
  39.   
  40.             // 计划更新  
  41.             emit resultReady(nValue);  
  42.   
  43.             // 检验是不是终止  
  44.             {  
  45.                 QMutexLocker locker(&m_mutex);  
  46.                 if (m_bStopped)  
  47.                     break;  
  48.             }  
  49.             // locker超过范围并释放互斥锁  
  50.         }  
  51.     }  
  52. signals:  
  53.     void resultReady(int value);  
  54.   
  55. private:  
  56.     bool m_bStopped;  
  57.     QMutex m_mutex;  
  58. };  

自我扩展了叁个WorkerThread类。WorkerThread世袭自QThread类,重写了其run(卡塔尔(英语:State of Qatar)函数。能够以为,run(卡塔尔国函数就是新的线程供给实践的代码。在此边正是要实行那么些轮回,然后发出总括达成的实信号。而在按键点击的槽函数中,使用work(卡塔尔(英语:State of Qatar)中的workThread.start(卡塔尔函数运行一个线程(注意,这里不是run(卡塔尔(قطر‎函数)。再度运路程序,你会开采今后分界面已经不会被窒碍了。

支配速度栏增速

在运维进程栏的start_progressbar(卡塔尔国方法中,大家透过while循环来充实进程栏,每趟扩张0.0001,在此么的情事下,进程栏增加得非常快。其实,大家得以经过time模块的sleep(卡塔尔(英语:State of Qatar)方法冻结进程栏,以完毕减弱速度栏增速的魔法。
修改start_progressbar(卡塔尔(قطر‎方法如下:

图片 7

我们让进程栏的数值增加安息0.3秒,看看效果:

图片 8

进程栏的增速确实比从前缓慢了非常多。

怎么要加锁?不会细小略,是为了分享数据段操作的排外。
何时需求加锁?在产生资源角逐的时候,也正是说,八个线程有希望访谈同风流倜傥分享能源的时候。
当主线程调用stop(卡塔尔(英语:State of Qatar)更新m_bStopped的时候,run(卡塔尔(قطر‎函数也极有相当大可能正在访谈它(那个时候,他们处于分裂的线程),所以存在能源角逐,因而须求加锁,保险分享数据的安全性。

上述正是本文的全体内容,希望对大家的就学抱有助于,也盼望大家多多关照脚本之家。

使用QThread线程

PyQt中QtCore的QThread对象提供了贰个单身于阳台的办法来保管线程。
QThread对象通过调用run(卡塔尔方法来实施线程,暗许景况下,run(卡塔尔(英语:State of Qatar)方法通过调用exec(卡塔尔国方法来运营事件的大循环。

下边,大家经过三个进度栏流速计的身体力行,来演示一下QThread的行使。

笔者们新建贰个三番一次自QtCore.QThread的类RunThread(卡塔尔(قطر‎,并在里头定义八个run(卡塔尔(英语:State of Qatar)方法用来运转线程,定义二个stop(卡塔尔(英语:State of Qatar)方法用来终止线程。

图片 9

先是,大家通超过实际例化QtCore.pyqtSignal(int卡塔尔(قطر‎定义了多个新的时域信号counter_value,这一个值会用来更新速度栏。然后设置了RunThread(卡塔尔(قطر‎类中的一些暗中认可属性。

在run(卡塔尔方法中,大家经过贰个while循环来不断累计流速计的值,并且将丰富的计数器的值通过定义的频域信号counter_value发送出去。

在stop(卡塔尔国方法中,大家设置线程的运营情状为Fasle,然后利用QThread的terminate(卡塔尔(قطر‎方法确认保障线程完全的被停止。

然后,要选择那些速度栏流速計,大家还需求在主窗口类MainWindow(卡塔尔国中加上和改造生龙活虎番:

增多二个进度栏流量计方法progressbar_counter(卡塔尔(قطر‎,用来运行线程微处理器:

图片 10

增添一个速度栏设置格局set_progressbar(卡塔尔(قطر‎,用来设置进程栏的值:

图片 11

校订一下速度栏的开发银行方法start_progressbar(卡塔尔(قطر‎。删除while循环,增添进程栏计数器到此中(因为速度栏流量计中已经贯彻了while循环扩展数值):

图片 12

改过进程栏的停下方法stop_progressbar(卡塔尔(英语:State of Qatar),增多线程的stop(卡塔尔国方法用来终止线程:

图片 13

更正完结后,大家运路程序,看看效果:

图片 14

经过终点打字与印刷出来的音信,大家能够直观的看看QThread线程的管控。

本篇到此下马,固然文中有错误接待指正,假若有疑难迎接留言钻探。
感谢阅读!

Wechat大伙儿号:州的书生 同步更新

二.Qt5以后:requestInterruption() + isInterruptionRequested()
那四个接口是Qt5.x引进的,使用很实惠: