Linux Qt使用POSIX多线程条件变量、互斥锁(量)_cpongo9的博客-程序员秘密

今天团建,但是文章也要写。酒要喝好,文要写美,方为我辈程序员的全才之路。嘎嘎

 

之前一直在看POSIX的多线程编程,上个周末结合自己的理解,写了一个基于Qt的用条件变量同步线程的例子。故此来和大家一起分享,希望和大家一起交流。

 

提到线程,如果在UI编程中,总会和一些耗时操作联系在一起。Qt中处理耗时操作通常有两种方式,一种是将耗时操作放在线程中;另一种则是使用QApplication::processEvents(),防止阻塞UI。从更加通用的角度来讲,我是更倾向于线程的,但对于很多初学者来讲,线程还是有一定难度的。比如说需要对线程间共享的数据提供保护,使用互斥量同步、使用条件变量、使用读写锁同步等;各种同步方式用在什么情况下,开始编程时多线程使用的并不多,无法切身体会到这些问题,后来程序写的多了一点儿,慢慢接触到一些多线程的东西,并且自己也可以学习了相关知识,并用到实际程序中。好了,下面以一个实际的例子为背景,来说明Linux POSIX多线程的一些特性。

 

程序环境:ubuntu 14.04、 Qt 5.5.1、 Posix多线程(C的用法)

这里简单说下我为什么用Linux C的多线程,因为Qt的多线程编程对于一些线程的终止时含糊不清楚的,并且一个线程被终止后的资源是无法被清理的,所以我选择是相对底层的一些用法,以后有机会我还会添加线程取消和线程退出的操作。

 

我自己设定的场景是这样的,在UI主线程中通过界面手动向一个线程间共享的队列中push数据,而另外开启的一个线程则一直在while中pop数据,这算是一个变种的生产者和消费者模式吧。

至于条件变量、互斥量(也就是互斥锁)的初始化在这里不再详细说明,只说明一些相对重要的地方。

 

1. UI中向队列push数据(生产者生产数)

这是一个槽函数,当在lineEdit中回车后,则会触发该槽函数,由于该队列是线程间的共享数据,所以使用了互斥锁进行保护,即该槽操作数据的过程中如果有其他线程想要操作数据,则其他线程则会被阻塞,即访问一个已经被加锁的互斥量的线程会被阻塞。

void Widget::on_le_writeNum_returnPressed(){
          int status;
status = pthread_mutex_lock (&mp_processThread->m_structCondition.mutex); if (status != 0) err_abort (status, "Lock mutex");
QString num = ui->le_writeNum->text(); mp_processThread->queuePushData(num.toInt());
status = pthread_cond_signal (&mp_processThread->m_structCondition.cond);// status = pthread_cond_broadcast( &mp_processThread->m_structCondition.cond); if (status != 0) err_abort (status, "Signal condition");
status = pthread_mutex_unlock (&mp_processThread->m_structCondition.mutex); if (status != 0) err_abort (status, "Unlock mutex");}

2. 消费者线程pop数据

该线程使用的是Qt的moveToThread方法创建的线程,这里注意的是,整个类都运行在新的线程中。该槽函数随着线程的启动信号(start())发射后而一直进行while循环。首先对互斥量上锁,之后判断谓词状态,如果队列为空,则等待条件变量。等待条件变量时pthread_cond_wait()会自动释放互斥锁,这样其他线程才能够操作共享数据。从条件变量等待中醒来后,会再次获得互斥锁,以操作共享数据。共享数据被操作完成后,再次释放互斥锁。这是我们使用条件变量等待的一个操作流程,如果我们不使用条件变量等待会是怎样的呢?

void ProcessThread::slot_processData(){
          int status;
while(!mb_stopThread) { status = pthread_mutex_lock (&m_structCondition.mutex); if (status != 0) err_abort (status, "Lock mutex");
while(m_queue.empty()) //if queue is empty, wait contion { //使用条件变量等待 status = pthread_cond_wait(&m_structCondition.cond, &m_structCondition.mutex); // qDebug() << "pthread_cond_wait is block func!";
if (status != 0) { err_abort (status, "Wait on cond faild"); } }
while(!m_queue.empty()) { qDebug() << "queue mem is" << m_queue.back();
m_queue.pop(); }
status = pthread_mutex_unlock (&m_structCondition.mutex); if (status != 0) err_abort (status, "Unlock mutex");
}
}

3. 不使用条件变量等待

①不使用条件变量等待

如果不使用条件变量等待,则消费者线程在很大一部时间内几乎都是在执行while(1)无限循环,这是很占用CPU资源的,在ubuntu下,使用htop查看的效果如下:

屏蔽status = pthread_cond_wait(&m_structCondition.cond,

&m_structCondition.mutex);

我们看到,此时CPU是满负荷在运行的,这当然不是一个程序所应有的正常状态。

②使用条件变量的结果

此时我们看到CPU的占用率是很低的,这也是为什么使用条件变量的原因之一,让不满足的条件的线程挂起,而不是在浪费CPU资源。条件变量是 允许使用队列的线程之间交换队列状态信息的机制。那么当我们还没有掌握线程条件变量的用法时,又遇到这种情况时,该怎么做呢?简单,加个5ms的延时即可,5ms对我们来讲时间极短极短,但对计算机来讲,已经挺长时间了。

 

最后,当我们关掉UI窗口时,会有这样一句消息:

QThread: Destroyed while thread is still running

线程正在运行时就被破坏了,这个我们接下来会说,那就是如何退出线程、终止线程以及取消线程等操作了。

欢迎大家关注公众号一起交流!

如果需要整个工程源码,欢迎后台留言哦~

如果转载,请注明出处,禁止商业用途,感谢合作。

版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。
本文链接:https://blog.csdn.net/cpongo9/article/details/103351154

智能推荐

python winform自动化_autogui-记录并自动化Winform和WPF应用程序-Alex Lundberg,Richard Boettcher Getting Started Lice..._Screwberry的博客-程序员秘密

作者:Alex Lundberg,Richard Boettcher### 作者邮箱:[email protected],[email protected]### 首页:https://github.com/lundbird/AutoGui### 文档:None### 下载链接# AutoGuiAutoGui is a GUI Automation/Test too...

[Android学习]JSON的三种解析方式_andorid需要学习json吗_d_o_n_g2的博客-程序员秘密

一、什么是JSON?JSON是一种取代XML的数据结构,和xml相比,它更小巧但描述能力却不差,由于它的小巧所以网络传输数据将减少更多流量从而加快速度。JSON就是一串字符串 只不过元素会使用特定的符号标注。{} 双括号表示对象[] 中括号表示数组"" 双引号内是属性或值: 冒号表示后者是前者的值(这个值可以是字符串、数字、也可以是另一个数组或对象)

pyautogui 接收按键中断_胡说先森的博客-程序员秘密

好的。PyAutoGUI 模块提供了一个 pyautogui.PAUSE 参数,可以用来控制 PyAutoGUI 在执行操作时的暂停时间。您可以将其设置为任何数值(以秒为单位),以便在 PyAutoGUI 执行操作时使用足够的时间来接收按键中断。例如,您可以使用以下代码设置 PyAutoGUI 在执行操作时暂停 2 秒:import pyautoguipyautogui.PAUSE = ...

IDEA引入第三方jar_多多小老虎的博客-程序员秘密

在工作中利用activiti开发工单系统中,遇到一个环境问题引起的Bug。由于使用k8s docker 容器,我们最开始的基础镜像里面的jdk是oracle 1.8。然而过了几个礼拜,我们的服务统一升级了基础镜像。后面测试发现activiti里面不能自动使用javaScripeEngine。报错:can not found ScriptEngine for ‘JavaScript’。看到这个报错内心是崩溃的,查找statckflow,发现有一个哥们也是这个问题,但是他最后的解决办法是该用groovy 脚本引

Application、Session、Cookie和ViewState等对象保存信息的区别_conan8126的博客-程序员秘密

在ASP.NET中,有很多种保存信息的对象.例如:Application、Session、Cookie、ViewState和Cache等,那么它们有什么区别呢?每一种对象应用的环境是什么?为了更清楚的了解,我们总结出每一种对象应用的具体环境,如下表所示:方法信息量大小保存时间应用范围保存位置Applicat

树链剖分原理和实现_wbugw_的博客-程序员秘密

理解树链剖分就是将树分割成多条链,然后利用数据结构(线段树、树状数组等)来维护这些链。首先就是一些必须知道的概念:重结点:子树结点数目最多的结点;轻节点:除了重节点以外的所有子节点;重边:父亲结点和重结点连成的边;轻边:父亲节点和轻节点连成的边;重链:由多条重边连接而成的路径;轻链:由多条轻边连接而成的路径;比如上面这幅图中,用黑线连接的结点都是重结点,其余均是轻结点,2-...

随便推点

react生命周期的基本用法_ime33的博客-程序员秘密

转载于:https://www.jianshu.com/p/c9bc994933d5首先,看一下一个组件的构造import React,{ Component } from 'react';class Demo extends Component { constructor(props,context) { super(props,context) ...

velocity 学习,邮件发送模板的应用_使用velocity发送邮件模板_SanGetInstance的博客-程序员秘密

XXXX服务商店 若你看不到下面的内容,请点击此处查看详细内容 尊敬的客户${user

使用清华thulac中文分词分析工具_thulac词性标注_徐孟奇的博客-程序员秘密

清华中文分词工具thulac使用记录1 安装2 使用2.1 遇到的问题解决2.2 使用分词和词性标注2.3 清除文本中的空行3 thulac使用方式3.1 使用示例3.2 接口参数3.3 guihub 地址1 安装由于用到了分词,需要给已经处理成每行内容只含汉字的txt文本进行分词,所以想到用thulac试一下。环境是anaconda+pycharm+python 3.6.使用pip安装:pip install thulac2 使用pycharm中新建python文件,导入thulac包,然

设计模式之间可以相互"功能替换"吗?_dhka8040652的博客-程序员秘密

有朋友看了我这篇文章后说模式并不存在相互转换,因为它们有不同的应用背景,我认为用转换的确不妥,那就改用功能替换吧,就是说一个功能的实现并不局限于一种模式. 在上一篇实际项目中的策略模式应用中,我分析了一下实际项目中如何应用策略模式,我们知道设计模式有二十多种,是不是在一个实现方法上只能唯一使用一种模式呢?它们之间是否有共同点,或者说它们之间是不是可以转换呢?...

js数组去重(含对象)_js数组去重包含对象_carfge的博客-程序员秘密

难点:对象是无序的关键:利用对象属性核心:对象根据key排序后通过JSON.stringify转换,构造唯一的对象属性function unique(arr){ var obj = {}; for(var i=0; i&lt;arr.length; i++){ var item = arr[i]; var t = typeof(item); if(t === 'object'){ var o = sortO

推荐文章

热门文章

相关标签