一、前言
前一篇文章提供了一个通过信号与主线程通信,实现多线程的程序,并在最初的多线程文章里提供了一篇来自Qt官方文档的多线程的示例。这篇文章主要是衔接上述两篇文章,对多线程这块内容继续补充,因为不管是C++编程,还是Qt编程,线程这块内容都是极为重要的,它能让应用程序不至于在处理大量事件时出现线程阻塞,以至于应用程序崩溃。如果上述两篇内容都没看过,可以快速点击下面的链接直达按需查阅。
前文《多线程(续)》文章:C++/Qt 多线程(续)-优快云博客 因为这里将会继续在提供的示例编程上加以补充,也就是将线程同步的编程加在原先的示例中,如需要,可配套查阅。
前文《多线程》文章:C++/Qt 多线程-优快云博客
二、线程同步
在多线程程序中,线程之间的通信和同步是重要的问题。Qt提供了QMutex、QWaitCondition、QSemaphore等多个类用于实现线程同步。
1、线程同步的概念
在多线程程序中,由于存在 多个线程,线程之间可能需要访问同一个变量,或一个线程需要等待另一个线程完成某个操作后才产生相应的动作。例如前文的例子中,工作线程生成随机的骰子点数,主线程读取骰子点数并显示,,主线程需要等待工作线程生成一个新的骰子点数后再读取数据。示例中使用了信号与槽的机制,在生成新的点数之后通过信号通知主线程读取新的数据。
如果不使用信号与槽且改变了计算变量m_diceValue的值的方法,TDiceThread的函数run()的代码变为如下内容:
void TDiceThread::run()
{
//线程的任务
m_stop = false;
m_paused = true; //启动线程后暂时不掷骰子
m_seq = 0;
while(!m_stop){ //循环体
if(!m_paused){
m_diceValue = 0;
for(int i = 0;i<5;i++) //对多个随机数求平均值
m_diceValue += QRandomGenerator::global()->bounded(1,7); //生成随机整数,范围为[1,6]
m_diceValue = m_diceValue/5;
m_seq++;
//emit newValue(m_seq,m_diceValue); //发射信号
}
msleep(500);
}
//在m_stop = true 时结束线程任务
quit();
}
TDiceThread类需要定义一个公有函数返回m_diceValue的值,以便在主程序中调用此函数读取骰子的点数,如:
int TDiceThread::diceValue()
{
return m_diceThread;
}
由于没有使用信号与槽的关联机制,主线程只能采用不断查询的方式主动查询是否有新数据,并在有新数据时读取新数据,但是在主线程调用函数diceThread()读取骰子点数时,工作线程可能正在运行run()函数中修改m_diceValue值得语句,即可能正在运行if语句中得关键代码。
这段代码可能需要运行较长的事件,且会不断修改变量m_diceValue的值,运行这段代码时不能被主线程调用的diceValue()函数打断,因为如果被打断,函数diceValue()返回的值可能是错误的。
因此,这段代码是需要被保护起来的,其在运行过程中不能被其它线程打断,以确保计算结果的完整性,这就涉及线程同步的问题。