C/C++ Qt QThread 线程组件应用
QThread库是QT中提供的跨平台多线程实现方案,使用时需要继承QThread这个基类,并重写实现内部的Run方法,由于该库是基本库,默认依赖于QtCore.dll这个基础模块,在使用时无需引入其他模块.
实现简单多线程: QThread库提供了跨平台的多线程管理方案,通常一个QThread对象管理一个线程,在使用是需要从QThread类继承并重写内部的Run方法,并在Run方法内部实现多线程代码.
#include <QCoreApplication>
#include <iostream>
#include <QThread>
class MyThread: public QThread
{
protected:
volatile bool m_to_stop;
protected:
// 线程函数必须使用Run作为开始
void run()
{
for(int x=0; !m_to_stop && (x <10); x++)
{
msleep(1000);
std::cout << objectName().toStdString() << std::endl;
}
}
public:
MyThread()
{
m_to_stop = false;
}
// 用于设置结束符号为真
void stop()
{
m_to_stop = true;
}
// 输出线程运行状态
void is_run()
{
std::cout << "Thread Running = " << isRunning() << std::endl;
}
// 输出线程完成状态(是否结束)
void is_finish()
{
std::cout << "Thread Finished = " << isFinished() << std::endl;
}
};
int main(int argc, char *argv[])
{
QCoreApplication a(argc, argv);
// 定义线程数组
MyThread thread[10];
// 设置线程对象名字
for(int x=0;x<10;x++)
{
thread[x].setObjectName(QString("thread => %1").arg(x));
}
// 批量调用run执行
for(int x=0;x<10;x++)
{
thread[x].start();
thread[x].is_run();
thread[x].isFinished();
}
// 批量调用stop关闭
for(int x=0;x<10;x++)
{
thread[x].wait();
thread[x].stop();
thread[x].is_run();
thread[x].is_finish();
}
return a.exec();
}
向线程中传递参数: 线程在执行前可以通过调用MyThread中的自定义函数,并在函数内实现参数赋值,实现线程传参操作.
#include <QCoreApplication>
#include <iostream>
#include <QThread>
class MyThread: public QThread
{
protected:
int m_begin;
int m_end;
int m_result;
void run()
{
m_result = m_begin + m_end;
}
public:
MyThread()
{
m_begin = 0;
m_end = 0;
m_result = 0;
}
// 设置参数给当前线程
void set_value(int x,int y)
{
m_begin = x;
m_end = y;
}
// 获取当前线程名
void get_object_name()
{
std::cout << "this thread name => " << objectName().toStdString() << std::endl;
}
// 获取线程返回结果
int result()
{
return m_result;
}
};
int main(int argc, char *argv[])
{
QCoreApplication a(argc, argv);
MyThread thread[3];
// 分别将不同的参数传入到线程函数内
for(int x=0; x<3; x++)
{
thread[x].set_value(1,2);
thread[x].setObjectName(QString("thread -> %1").arg(x));
thread[x].start();
}
// 等待所有线程执行结束
for(int x=0; x<3; x++)
{
thread[x].get_object_name();
thread[x].wait();
}
// 获取线程返回值并相加
int result = thread[0].result() + thread[1].result() + thread[2].result();
std::cout << "sum => " << result << std::endl;
return a.exec();
}
QMutex 互斥同步线程锁: QMutex类是基于互斥量的线程同步锁,该锁lock()锁定与unlock()解锁必须配对使用,线程锁保证线程间的互斥,利用线程锁能够保证临界资源的安全性.
- 线程锁解决的问题: 多个线程同时操作同一个全局变量,为了防止资源的无序覆盖现象,从而需要增加锁,来实现多线程抢占资源时可以有序执行.
- 临界资源(Critical Resource): 每次只允许一个线程进行访问 (读/写)的资源.
- 线程间的互斥(竞争): 多个线程在同一时刻都需要访问临界资源.
- 一般性原则: 每一个临界资源都需要一个线程锁进行保护.
#include <QCoreApplication>
#include <iostream>
#include <QThread>
#include <QMutex>
static QMutex g_mutex; // 线程锁
static QString g_store; // 定义全局变量
class Producer : public QThread
{
protected:
void run()
{
int count = 0;
while(true)
{
// 加锁
g_mutex.lock();
g_store.append(QString::number((count++) % 10));
std::cout << "Producer -> "<< g_store.toStdString() << std::endl;
// 释放锁
g_mutex.unlock();
msleep(900);
}
}
};
class Customer : public QThread
{
protected:
void run()
{
while( true )
{
g_mutex.lock();
if( g_store != "" )
{
g_store.remove(0, 1);
std::cout << "Curstomer -> "<< g_store.toStdString() << std::endl;
}
g_mutex.unlock();
msleep(1000);
}
}
};
int main(int argc, char *argv[])
{
QCoreApplication a(argc, argv);
Producer p;
Customer c;
p.setObjectName("producer");
c.setObjectName("curstomer");
p.start();
c.start();
return a.exec();
}
QMutexLocker是在QMutex基础上简化版的线程锁,QMutexLocker会保护加锁区域,并自动实现互斥量的锁定和解锁操作,可以将其理解为是智能版的QMutex锁,该锁只需要在上方代码中稍加修改即可.
#include <QMutex>
#include <QMutexLocker>
static QMutex g_mutex; // 线程锁
static QString g_store; // 定义全局变量
class Producer : public QThread
{
protected:
void run()
{
int count = 0;
while(true)
{
// 增加智能线程锁
QMutexLocker Locker(&g_mutex);
g_store.append(QString::number((count++) % 10));
std::cout << "Producer -> "<< g_store.toStdString() << std::endl;
msleep(900);
}
}
};
互斥锁存在一个问题,每次只能有一个线程获得互斥量的权限,如果在程序中有多个线程来同时读取某个变量,那么使用互斥量必须排队,效率上会大打折扣,基于QReadWriteLock读写模式进行代码段锁定,即可解决互斥锁存在的问题.
QReadWriteLock 读写同步线程锁: 该锁允许用户以同步读lockForRead()或同步写lockForWrite()两种方式实现保护资源,但只要有一个线程在以写的方式操作资源,其他线程也会等待写入操作结束后才可继续读资源.
#include <QCoreApplication>
#include <iostream>
#include <QThread>
#include <QMutex>
#include <QReadWriteLock>
static QReadWriteLock g_mutex; // 线程锁
static QString g_store; // 定义全局变量
class Producer : public QThread
{
protected:
void run()
{
int count = 0;
while(true)
{
// 以写入方式锁定资源
g_mutex.lockForWrite();
g_store.append(QString::number((count++) % 10));
// 写入后解锁资源
g_mutex.unlock();
msleep(900);
}
}
};
class Customer : public QThread
{
protected:
void run()
{
while( true )
{
// 以读取方式写入资源
g_mutex.lockForRead();
if( g_store != "" )
{
std::cout << "Curstomer -> "<< g_store.toStdString() << std::endl;
}
// 读取到后解锁资源
g_mutex.unlock();
msleep(1000);
}
}
};
int main(int argc, char *argv[])
{
QCoreApplication a(argc, argv);
Producer p1,p2;
Customer c1,c2;
p1.setObjectName("producer 1");
p2.setObjectName("producer 2");
c1.setObjectName("curstomer 1");
c2.setObjectName("curstomer 2");
p1.start();
p2.start();
c1.start();
c2.start();
return a.exec();
}
QSemaphore 基于信号线程锁: 信号量是特殊的线程锁,信号量允许N个线程同时访问临界资源,通过acquire()获取到指定资源,release()释放指定资源.
#include <QCoreApplication>
#include <iostream>
#include <QThread>
#include <QSemaphore>
const int SIZE = 5;
unsigned char g_buff[SIZE] = {0};
QSemaphore g_sem_free(SIZE); // 5个可生产资源
QSemaphore g_sem_used(0); // 0个可消费资源
// 生产者生产产品
class Producer : public QThread
{
protected:
void run()
{
while( true )
{
int value = qrand() % 256;
// 若无法获得可生产资源,阻塞在这里
g_sem_free.acquire();
for(int i=0; i<SIZE; i++)
{
if( !g_buff[i] )
{
g_buff[i] = value;
std::cout << objectName().toStdString() << " --> " << value << std::endl;
break;
}
}
// 可消费资源数+1
g_sem_used.release();
sleep(2);
}
}
};
// 消费者消费产品
class Customer : public QThread
{
protected:
void run()
{
while( true )
{
// 若无法获得可消费资源,阻塞在这里
g_sem_used.acquire();
for(int i=0; i<SIZE; i++)
{
if( g_buff[i] )
{
int value = g_buff[i];
g_buff[i] = 0;
std::cout << objectName().toStdString() << " --> " << value << std::endl;
break;
}
}
// 可生产资源数+1
g_sem_free.release();
sleep(1);
}
}
};
int main(int argc, char *argv[])
{
QCoreApplication a(argc, argv);
Producer p1;
Customer c1;
p1.setObjectName("producer");
c1.setObjectName("curstomer");
p1.start();
c1.start();
return a.exec();
}
C/C++ Qt QThread 线程组件应用的更多相关文章
- Qt QThread 线程创建,线程同步,线程通信 实例
1. 继承QThread, 实现run()方法, 即可创建线程. 2. 实例1 代码 myThread.h #ifndef MYTHREAD_H #define MYTHREAD_H #includ ...
- Qt经典—线程、事件与Qobject(耳目一新)
介绍 You’re doing it wrong. — Bradley T. Hughes 线程是qt channel里最流行的讨论话题之一.许多人加入了讨论并询问如何解决他们在运行跨线程编程时所遇到 ...
- Qt经典—线程、事件与Qobject
介绍 You’re doing it wrong. — Bradley T. Hughes 线程是qt channel里最流行的讨论话题之一.许多人加入了讨论并询问如何解决他们在运行跨线程编程时所遇到 ...
- Qt同步线程(比较清楚,而且QMutex QMutexLocker QReadWriteLock QSemaphore QWaitCondition 每个都有例子)
Qt同步线程 我们知道,多线程有的时候是很有用的,但是在访问一些公共的资源或者数据时,需要进行同步,否则会使数据遭到破坏或者获取的值不正确.Qt提供了一些类来实现线程的同步,如QMutex,QMute ...
- [转]QT子线程与主线程的信号槽通信-亲测可用!
近用QT做一个服务器,众所周知,QT的主线程必须保持畅通,才能刷新UI.所以,网络通信端采用新开线程的方式.在涉及到使用子线程更新Ui上的控件时遇到了点儿麻烦.网上提供了很多同一线程不同类间采用信号槽 ...
- 界面编程之QT的线程20180731
/*******************************************************************************************/ 一.为什么需 ...
- 重点:怎样正确的使用QThread类(注:包括推荐使用QThread线程的新方法QObject::moveToThread)
背景描述: 以前,继承 QThread 重新实现 run() 函数是使用 QThread唯一推荐的使用方法.这是相当直观和易于使用的.但是在工作线程中使用槽机制和Qt事件循环时,一些用户使用错了.Qt ...
- Qt 子线程更新Ui
最近做练习,写一个Qt版的飞机大战,需要用子线程更新UI,发现Qt子线程不能更新Ui,否则程序会崩溃.在网上百度了下,说是需要在子线程自定义信号,然后在线程回调的run()函数里发射信号,主线程连接信 ...
- Qt同步线程(QMutex QMutexLocker QReadWriteLock QSemaphore QWaitCondition )
Qt同步线程 我们知道,多线程有的时候是很有用的,但是在访问一些公共的资源或者数据时,需要进行同步,否则会使数据遭到破坏或者获取的值不正确.Qt提供了一些类来实现线程的同步,如QMutex,QMute ...
- QThread 线程暂停 停止功能的实现
为了实现Qt中线程的暂停运行,和停止运行的控制功能 需要在设置两个static型控制变量. //终止原始数据单元进队出队,并清空数据. static bool stopSign; //原始数据单元队列 ...
随机推荐
- 【计算机网络】身份认证Oauth2
身份认证Oauth2 https://www.bilibili.com/video/BV1FL411h7es/?spm_id_from=333.999.0.0&vd_source=d11276 ...
- UVA - 10935:Throwing cards away I (简单模拟)
题目大意 桌上有一叠牌,自上而下编号为1~n.若桌上牌数大于1张,那么丢弃一张顶部牌后,再将现在的顶部牌移到最后.要求给出模拟过程和最终剩余的牌号 思路分析 典型队列模拟,丢弃即出队,移到最后即入队, ...
- Codeforces Round #481 (Div. 3) 经典几道思维题
A - AAA POJ - 3321 给你一颗树,支持两种操作 1.修改某一节点的权值 2.查询子树的权值(子树中节点的个数) 很显然可以用树状数组/线段树维护 B - BBB CodeForces ...
- 第六届蓝桥杯C++A组 A~F题题解
蓝桥杯历年国赛真题汇总:Here 1.方格填数 在2行5列的格子中填入1到10的数字. 要求: 相邻的格子中的数,右边的大于左边的,下边的大于上边的. 如[图1.png]所示的2种,就是合格的填法. ...
- AtCoder Beginner Contest 197(Sponsored by Panasonic) Person Editorial
A - Rotate 先输出第二和第三个字符,然后再输出第一个字符即可 B - Visibility 以 \((x,y)\) 作为起点向4个方向探索不是 # 的点,注意一下会在\((x,y)\)重复计 ...
- 图解 Promise 实现原理(二)—— Promise 链式调用
本文首发于 vivo互联网技术 微信公众号 链接: https://mp.weixin.qq.com/s/Xz2bGaLxVL4xw1M2hb2nJQ作者:Morrain 很多同学在学习 Promis ...
- 虚拟现实 VR 碰撞 3D 可视化,图扑打造一体化管控平台
前言 工信部<虚拟现实产业发展白皮书 5.0 >中明确提出:"通过财政资金促进虚拟现实技术产业化,支持面向工业.文化.教育等重点行业的虚拟现实技术应用". 虚拟现实 V ...
- 处理uniapp(同理小程序)开发中使用rich-text富文本解析,图片未自适应宽度问题(图片显示不全)
https://www.cnblogs.com/luyaru/p/15538883.html
- 大数相加 a+b
#include<stdio.h> #include<string.h> #include<stdlib.h> #include<ctype.h> #i ...
- 如何在 Photoshop 中制作 GIF 动画
您可能已经使用过一些在线 gif 生成器来快速生成 gif 图像,但这些在线生成器无法提供与 Photoshop 相同的灵活性和结果. gif 就像您可以在 Photoshop 中创建的迷你动画.当你 ...