文章目录
一、QSemaphore使用示例图
1.1 QSemaphore基本使用示例图
下图为信号量的基本使用示例图,每点击一次按钮则创建一个资源,源码在本文第三节(源码含详细注释)。
2.2 QSemaphore循环输出ABC示例图
下图为信号量的基本使用示例图,点击启动线程则开始循环输出ABC,源码在本文第四节
二、信号量、QSemaphore(个人理解)
了解生产者消费者模型(一种设计模式)的会发现信号量和该设计模型有一定类似。顾名思义生产者消费者模型是生产者生产资源,消费者消费资源;而信号量就像如此,存在生产资源和消费资源的情况,生产资源使用release函数,消费资源使用acquire函数(并且可以指定生产和消费的资源个数)
三、QSemaphore的基本使用(源码)
3.1 CThread定义类
CThread.h
#ifndef CTHREAD_H
#define CTHREAD_H
#include <QObject>
#include <QThread>
#include <QSemaphore>
class CThread : public QThread
{
Q_OBJECT
public:
explicit CThread(QObject *parent = nullptr);
~CThread();
void run();
QSemaphore *sem() const;
private:
QSemaphore * m_sem; //定义一个信号量指针
};
#endif // CTHREAD_H
CThread.cpp
#include "CThread.h"
#include <QDebug>
CThread::CThread(QObject *parent)
: QThread(parent)
{
m_sem = new QSemaphore; //new出信号量,在创建信号量时可指定一个int值为初始已存在的资源数
}
CThread::~CThread()
{
delete m_sem;
}
void CThread::run()
{
int i = 0;
while(i++ != 3)
{
m_sem->acquire(); //获取一个资源,当资源不够时将阻塞,直到资源足够
//输出线程id并显示i值
qDebug() << "线程" << QThread::currentThreadId() << QString("获取了第%1个资源").arg(i);
}
qDebug() << "线程" << QThread::currentThreadId() << "循环结束";
}
QSemaphore *CThread::sem() const
{
return m_sem;
}
3.2 CMainWindow调用类
CMainWindow.h
#ifndef CMAINWINDOW_H
#define CMAINWINDOW_H
#include <QMainWindow>
#include "CThread.h"
namespace Ui {
class CMainWindow;
}
class CMainWindow : public QMainWindow
{
Q_OBJECT
public:
explicit CMainWindow(QWidget *parent = 0);
~CMainWindow();
private slots:
void on_startBtn_clicked(); //创建资源的槽函数
private:
Ui::CMainWindow *ui;
CThread *m_cThread; //线程指针
};
#endif // CMAINWINDOW_H
CMainWindow.cpp
#include "CMainWindow.h"
#include "ui_CMainWindow.h"
CMainWindow::CMainWindow(QWidget *parent) :
QMainWindow(parent),
ui(new Ui::CMainWindow)
{
ui->setupUi(this);
//new出CThread对象
m_cThread = new CThread;
m_cThread->start(); //启动线程
}
CMainWindow::~CMainWindow()
{
m_cThread->exit();
m_cThread->wait(1);
delete m_cThread;
delete ui;
}
void CMainWindow::on_startBtn_clicked()
{
//获取信号量变量,并创建一个资源
m_cThread->sem()->release(1);
}
四、QSemaphore实现循环输出ABC
4.1 CThread定义类
CThread.h
#ifndef CTHREAD_H
#define CTHREAD_H
#include <QObject>
#include <QThread>
#include <QSemaphore>
class CThread : public QThread
{
Q_OBJECT
public:
explicit CThread(int n, QObject *parent = nullptr);
~CThread();
void run();
QSemaphore *currentSem() const;
void setNextSem(QSemaphore *nextSem);
void setFlag(char flag);
private:
QSemaphore *m_currentSem; //定义当前线程的信号量指针
QSemaphore *m_nextSem; //定义下次运行线程的信号量指针
char m_flag; //定义char变量存储当前线程的字符
};
#endif // CTHREAD_H
CThread.cpp
#include "CThread.h"
#include <QDebug>
CThread::CThread(int n, QObject *parent)
: QThread(parent)
{
m_currentSem = new QSemaphore(n); //new出信号量,并指定默认资源数
}
CThread::~CThread()
{
delete m_currentSem;
}
void CThread::run()
{
int i = 0;
//循环输出当前字符七次
while(i++ != 7)
{
m_currentSem->acquire(); //获取资源
//输出线程id并显示i值
qDebug() << m_flag << QThread::currentThreadId();
QThread::usleep(300000); //使线程睡眠一段时间(单位:微秒),作用为减缓输出速度
m_nextSem->release(1); //为下次运行的线程创建资源
}
qDebug() << "线程" << QThread::currentThreadId() << "循环结束";
}
QSemaphore *CThread::currentSem() const
{
return m_currentSem;
}
void CThread::setNextSem(QSemaphore *nextSem)
{
m_nextSem = nextSem;
}
void CThread::setFlag(char flag)
{
m_flag = flag;
}
4.2 CMainWindow调用类
CMainWindow.h
#ifndef CMAINWINDOW_H
#define CMAINWINDOW_H
#include <QMainWindow>
#include "CThread.h"
namespace Ui {
class CMainWindow;
}
class CMainWindow : public QMainWindow
{
Q_OBJECT
public:
explicit CMainWindow(QWidget *parent = 0);
~CMainWindow();
private slots:
void on_startBtn_clicked(); //按钮槽函数(环形唤醒)
private:
Ui::CMainWindow * ui;
QList<CThread *> m_threadList; //线程指针容器
};
#endif // CMAINWINDOW_H
CMainWindow.cpp
#include "CMainWindow.h"
#include "ui_CMainWindow.h"
CMainWindow::CMainWindow(QWidget *parent) :
QMainWindow(parent),
ui(new Ui::CMainWindow)
{
ui->setupUi(this);
//使用循环为线程链表添加三个线程并运行
for(int index = 0; index != 3; ++index)
{
//0 == index? 1: 0,此处为三目运算符,判断条件为0 == index,为true则返回?后面的值,反之返回:后面的值
m_threadList.append(new CThread(0 == index? 1: 0));
m_threadList[index]->setFlag(65 + index); //设置标识符
}
//将获各个线程的m_currentSem设置到对应的存储位置中
//!这里可以如此理解 (0->1:代表0中创建1的资源)
//! 0->1,1->2,2->0
//! 如此看来则形成了环状的资源创建关系
m_threadList[0]->setNextSem(m_threadList[1]->currentSem());
m_threadList[1]->setNextSem(m_threadList[2]->currentSem());
m_threadList[2]->setNextSem(m_threadList[0]->currentSem());
}
CMainWindow::~CMainWindow()
{
foreach (CThread *thread, m_threadList)
{
thread->quit();
thread->wait(1);
delete thread;
}
delete ui;
}
void CMainWindow::on_startBtn_clicked()
{
//启动三个线程
m_threadList[0]->start();
m_threadList[1]->start();
m_threadList[2]->start();
}
总结
信号量相对互斥锁来说会简单一点,个人感觉信号量和生产者消费者模型非常类似,理解起来也相对简单。因为我信号量使用较少,目前就总结这点叭
相关文章
启动QThread线程的两种方法(含源码+注释)
Qt互斥锁(QMutex)、条件变量(QWaitCondition)讲解+QMutex实现多线程循环输出ABC(含源码+注释)
Qt互斥锁(QMutex)的使用、QMutexLocker的使用(含源码+注释)
QRunnable线程、QThreadPool(线程池)的使用(含源码+注释)
Qt读写锁(QReadWriteLock)的使用、读写锁的验证(含源码+注释)
Qt读写锁(QWriteLocker、QReadLocker)的理解和使用(含部分源码)
Qt之线程运行指定函数(含源码+注释,优化速率)
友情提示——哪里看不懂可私哦,让我们一起互相进步吧
(创作不易,请留下一个免费的赞叭 谢谢 ^o^/)
注:文章为作者编程过程中所遇到的问题和总结,内容仅供参考,若有错误欢迎指出。
注:如有侵权,请联系作者删除