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

之前一直在看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

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

欢迎大家一起交流!

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

Linux Qt使用POSIX多线程条件变量、互斥锁(量)的更多相关文章

  1. 笔记3 linux 多线程 条件变量+互斥锁

    //cond lock #include<stdio.h> #include<unistd.h> #include<pthread.h> struct test { ...

  2. posix多线程--条件变量

    条件变量是用来通知共享数据状态信息的. 1.条件变量初始化两种方式:(1)静态初始化pthread_cond_t cond = PTHREAD_COND_INITIALIZER;代码示例如下: #in ...

  3. 【Linux C 多线程编程】互斥锁与条件变量

    一.互斥锁 互斥量从本质上说就是一把锁, 提供对共享资源的保护访问. 1) 初始化: 在Linux下, 线程的互斥量数据类型是pthread_mutex_t. 在使用前, 要对它进行初始化: 对于静态 ...

  4. Linux 多线程条件变量同步

    条件变量是线程同步的另一种方式,实际上,条件变量是信号量的底层实现,这也就意味着,使用条件变量可以拥有更大的自由度,同时也就需要更加小心的进行同步操作.条件变量使用的条件本身是需要使用互斥量进行保护的 ...

  5. Linux 线程同步的三种方法(互斥锁、条件变量、信号量)

    互斥锁 #include <cstdio> #include <cstdlib> #include <unistd.h> #include <pthread. ...

  6. C++11并发——多线程条件变量std::condition_variable(四)

    https://www.jianshu.com/p/a31d4fb5594f https://blog.csdn.net/y396397735/article/details/81272752 htt ...

  7. posix thread条件变量

    概述 等待条件变量总是返回锁住的互斥量. 条件变量的作用是发送信号,而不是互斥. 与条件变量相关的共享数据是“谓词”,如队列满或队列空条件. 一个条件变量应该与一个谓词相关.如果一个条件变量与多个谓词 ...

  8. linux多线程编程之互斥锁

    多线程并行运行,共享同一种互斥资源时,需要上互斥锁来运行,主要是用到pthread_mutex_lock函数和pthread_mutex_unlock函数对线程进行上锁和解锁 下面是一个例子: #in ...

  9. 四十二、Linux 线程——线程同步之条件变量之线程状态转换

    42.1 线程状态转换 42.1.1 状态转换图 42.1.2 一个线程计算,多个线程获取的案例 #include <stdio.h> #include <stdlib.h> ...

随机推荐

  1. Singleton and Prototype Bean Scope in Spring

    Scope描述的是Spring容器如何新建Bean的实例的. 1> Singleton: 一个Spring容器只有一个Bean的实例,此为Spring的默认配置,全容器共享一个实例. 2> ...

  2. 通用shell函数库

    1.输出字体颜色库 #!/bin/bash export black='\E[0m\c' export boldred='\E[1;31m\c' export boldgreen='\E[1;32m\ ...

  3. leadcode的Hot100系列--二叉树创建和遍历

    很多题目涉及到二叉树,所以先把二叉树的一些基本的创建和遍历写一下,方便之后的本地代码调试. 为了方便,这里使用的数据为char类型数值,初始化数据使用一个数组. 因为这些东西比较简单,这里就不做过多详 ...

  4. yarn or npm 版本固化如何选择

    前言 作为前端开发者,npm这个包管理工具的重要性显而易见.优点不再表述,但一些缺点是为使用者诟病比较多的:速度慢.版本控制.下面主要讨论下npm的版本固化问题,即lock文件. npm语义化版本管理 ...

  5. 001-python3 初识

    一.python的起源 python是一门 解释型弱类型编程语言. 特点: 简单.明确.优雅 二.python的解释器 CPython. 官方提供的. 内部使用c语言来实现 PyPy. 一次性把我们的 ...

  6. java基础类型源码解析之HashMap

    终于来到比较复杂的HashMap,由于内部的变量,内部类,方法都比较多,没法像ArrayList那样直接平铺开来说,因此准备从几个具体的角度来切入. 桶结构 HashMap的每个存储位置,又叫做一个桶 ...

  7. NEUOJ 1702:撩妹全靠魅力值(CDQ分治三维偏序)

    http://acm.neu.edu.cn/hustoj/problem.php?id=1702 思路:三维偏序模板题,用CDQ分治+树状数组或者树套树.对于三元组(x,y,z),先对x进行排序,然后 ...

  8. Yarn原理

    欢迎关注我的公众号<小沈干货>,谢谢大家. 一.可以将YARN看作是分布式操作系统,在大数据组件中,YARN的定位是: 1)集群资源管理中心 2)任务调度中心 YARN的功能进一步可以细化 ...

  9. Coder必须自废的两样神功

    Coder必须自废的两样神功 大理段氏以一阳指神功驰名天下.奉六脉宝经,立天龙佛院:凭借数百载基业威名,与嵩山少林.终南全真分庭抗礼:乃宋代中华武术三大派系之一. 二指禅是一种鲜为人知的秘传功法,通过 ...

  10. kuangbin专题专题四 Frogger POJ - 2253

    题目链接:https://vjudge.net/problem/POJ-2253 思路: 从一号到二号石头的所有路线中,每条路线中都个子选出该路线中两点通路的最长距离,并在这些选出的最长距离选出最短路 ...