如果一个线程运行完成,就会结束。可很多情况并非这么简单,由于某种特殊原因,当线程还未执行完时,我们就想中止它。
不恰当的中止往往会引起一些未知错误。比如:当关闭主界面的时候,很有可能次线程正在运行,这时,就会出现如下提示:
QThread: Destroyed while thread is still running
这是因为次线程还在运行,就结束了UI主线程,导致事件循环结束。这个问题在使用线程的过程中经常遇到,尤其是耗时操作。
在此问题上,常见的两种人:
1.直接忽略此问题。
2.强制中止 - terminate()。
大多数情况下,当程序退出时,次线程也许会正常退出。这时,虽然抱着侥幸心理,但隐患依然存在,也许在极少数情况下,就会出现Crash。
正如前面提到过terminate(),比较危险,不鼓励使用。线程可以在代码执行的任何点被终止。线程可能在更新数据时被终止,从而没有机会来清理自己,解锁等等。。。总之,只有在绝对必要时使用此函数。
所以,我们应该采取合理的措施来优雅地结束线程,一般思路:
1.发起线程退出操作,调用quit()或exit()。
2.等待线程完全停止,删除创建在堆上的对象。
3.适当的使用wait()(用于等待线程的退出)和合理的算法。
下面介绍两种方式:
一.QMutex互斥锁 + bool成员变量。
这种方式是Qt4.x中比较常用的,主要是利用“QMutex互斥锁 + bool成员变量”的方式来保证共享数据的安全性(可以完全参照下面的requestInterruption()源码写法)。

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

为什么要加锁?很简单,是为了共享数据段操作的互斥。
何时需要加锁?在形成资源竞争的时候,也就是说,多个线程有可能访问同一共享资源的时候。
当主线程调用stop()更新m_bStopped的时候,run()函数也极有可能正在访问它(这时,他们处于不同的线程),所以存在资源竞争,因此需要加锁,保证共享数据的安全性。

二.Qt5以后:requestInterruption() + isInterruptionRequested()
这两个接口是Qt5.x引入的,使用很方便:

  1. class WorkerThread : public QThread
  2. {
  3. Q_OBJECT
  4. public:
  5. explicit WorkerThread(QObject *parent = 0)
  6. : QThread(parent)
  7. {
  8. }
  9. ~WorkerThread() {
  10. // 请求终止
  11. requestInterruption();
  12. quit();
  13. wait();
  14. }
  15. protected:
  16. virtual void run() Q_DECL_OVERRIDE {
  17. // 是否请求终止
  18. while (!isInterruptionRequested())
  19. {
  20. // 耗时操作
  21. }
  22. }
  23. };

在耗时操作中使用isInterruptionRequested()来判断是否请求终止线程,如果没有,则一直运行;当希望终止线程的时候,调用requestInterruption()即可。
正如侯捷所言:「源码面前,了无秘密」。如果还心存疑虑,我们不妨来看看requestInterruption()、isInterruptionRequested()的源码:

  1. void QThread::requestInterruption()
  2. {
  3. Q_D(QThread);
  4. QMutexLocker locker(&d->mutex);
  5. if (!d->running || d->finished || d->isInFinish)
  6. return;
  7. if (this == QCoreApplicationPrivate::theMainThread) {
  8. qWarning("QThread::requestInterruption has no effect on the main thread");
  9. return;
  10. }
  11. d->interruptionRequested = true;
  12. }
  13. bool QThread::isInterruptionRequested() const
  14. {
  15. Q_D(const QThread);
  16. QMutexLocker locker(&d->mutex);
  17. if (!d->running || d->finished || d->isInFinish)
  18. return false;
  19. return d->interruptionRequested;
  20. }

^_^,内部实现居然也用了互斥锁QMutex,这样我们就可以放心地使用了。

原文链接:http://blog.csdn.net/liang19890820/article/details/52186626

http://blog.csdn.net/caoshangpa/article/details/62421334

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

  1. [转]Qt中定时器使用的两种方法

    Qt中定时器的使用有两种方法,一种是使用QObject类提供的定时器,还有一种就是使用QTimer类. 其精确度一般依赖于操作系统和硬件,但一般支持20ms.下面将分别介绍两种方法来使用定时器. 方法 ...

  2. hive权威安装出现的不解错误!(完美解决)两种方法都可以

    以下两种方法都可以,推荐用方法一! 方法一: 步骤一: yum -y install mysql-server 步骤二:service mysqld start 步骤三:mysql -u root - ...

  3. QT 实现图片旋转的两种方法

    第一种方案 使用 QPixmap 的 transformed 函数来实现旋转,这个函数默认是以图片中心为旋转点,不能设置旋转的中心点,使用如下: QMatrix matrix; matrix.rota ...

  4. delphi 程序强制结束自身(两种方法都暴力)

    procedure KillSelf;begin  Sleep(1000);  if not TerminateProcess(GetCurrentProcessId, 0) then  WinExe ...

  5. Qt之自定义托盘(两种方法)

    http://www.cnblogs.com/swarmbees/p/5789482.html http://www.cnblogs.com/swarmbees/p/5812031.html

  6. Intellij IDEA实现SpringBoot项目多端口启动的两种方法

    有时候使用springboot项目时遇到这样一种情况,用一个项目需要复制很多遍进行测试,除了端口号不同以外,没有任何不同.遇到这种情况怎么办呢?这时候可以使用Intellij IDEA解决 前言 有时 ...

  7. android 之 启动画面的两种方法

    现在,当我们打开任意的一个app时,其中的大部分都会显示一个启动界面,展示本公司的logo和当前的版本,有的则直接把广告放到了上面.启动画面的可以分为两种设置方式:一种是两个Activity实现,和一 ...

  8. Loadrunner 接口测试的两种方法

    其实无论用那种测试方法,接口测试的原理是通过测试程序模拟客户端向服务器发送请求报文,服务器接收请求报文后对相应的报文做出处理然后再把应答报文发送给客户端,客户端接收应答报文这一个过程. 方法一.用Lo ...

  9. Ajax中解析Json的两种方法详解

    eval();  //此方法不推荐 JSON.parse();  //推荐方法 一.两种方法的区别 我们先初始化一个json格式的对象: var jsonDate = '{ "name&qu ...

随机推荐

  1. 查询系统状态 内存大小 cpu信息 设备负载情况

    1.1 查看内存状态 /proc/meminfo里面存放着内存的信息 查看内存命令(包括虚拟内存swap): free -h (低版本系统可能不支持-h) 或者 free -m (以mb单位显示) a ...

  2. Cordova之打包签名apk

    首先是关于apk签名,Android程序的安装是以包名(package name)进行区分的,就是同样的包名会被认作是同一个程序.这样就可以进行升级.替换.但是包名是一个可以被查看的字符串,这样就可能 ...

  3. Shell Step by Step (3) —— Stdin &amp; if

    4.输入输出 #! /bin/bash # Read users input and then get his name read -p "Please input your first n ...

  4. [Scikit-Learn] - 数据预处理 - 缺失值(Missing Value)处理

    reference : http://www.cnblogs.com/chaosimple/p/4153158.html 关于缺失值(missing value)的处理 在sklearn的prepro ...

  5. 各个版本 Windows 10 系统中自带的 .NET Framework 版本

    原文各个版本 Windows 10 系统中自带的 .NET Framework 版本 Windows 名称 Windows 版本 自带的 .NET Framework 版本 Windows 10 Oc ...

  6. Python入门(good)

    Python缩进和冒号对于Python而言代码缩进是一种语法,Python没有像其他语言一样采用{}或者begin...end分隔代码块,而是采用代码缩进和冒号来区分代码之间的层次. 缩进的空白数量是 ...

  7. Linux性能测试 uptime命令

    uptime 命令用于查看服务器运行了多长时间以及有多少个用户登录,快速获知服务器的负荷情况. 以下是 uptime 的运行实例: :: up days, min, users, load avera ...

  8. VS2005下第一个ATL

    作者:kagula 日期:  2008-9-2 环境: [1]VisualStudio2005简体中文版(必需已经安装C语言开发环境支持) [2]WinXP+SP3 读者要求: 初步使用过Visual ...

  9. error C2220: 警告被视为错误 - 没有生成“object”文件

    原文:error C2220: 警告被视为错误 - 没有生成"object"文件 这种错误的原因是:原因是该文件的代码页为英文,而我们系统中的代码页为中文.   解决方案: 1. ...

  10. 圆周卷积(circular convolution)

    1. 定义与概念 圆周卷积也叫循环卷积, 2. 实现(matlab) 以圆周的形式卷积两个信号: >> z = ifft(fft(x).*fft(y));