在编程中经常需要创建子线程来执行耗时的任务,避免主线程卡死,提高程序性能。其中有很多业务场景需要我们可以自由控制子线程的开启/暂停/终止。比如音视频播放器,除了用来操作ui控件的GUI主线程外,还有音视频数据解封装线程、视频解码线程、音频解码线程。用户按下“播发”按钮要能开始播放视频(开启所有子线程),用户按下“暂停”按钮要能暂停播放视频(暂停所有子线程”,用户按下“停止”按钮要能结束播放视频(终止所有子线程)。针对这种业务场景我们需要有个机制:当用户按下某个按钮后,GUI主线程可以通知所有的子线程开启/暂停/终止。我们可以使用观察者模式(发布订阅模式)来实现这种一对多的场景。观察者模式的精髓在于一对多的联动。一个对象的状态发生变化,则依赖的所有对象都会收到通知。
下面贴代码:
本示例使用了一个C++观察者模式的库:https://github.com/dacap/observable
按照下图所示把库里面的文件添加进visual studio工程里面
main.cpp
#include <stdio.h>
#include <atomic>
#include <thread>
#include <chrono>
#include <memory>
#include <iostream>
#include "obs.h"
using namespace std;
//子线程抽象类,为观察者基类
class BaseThread {
public:
enum class RunningStatus //子线程运行状态
{
START = 0, //开始运行/继续运行子线程
PAUSE = 1, //暂停子线程
STOP = 2 //终止子线程。
};
BaseThread()
{
m_pthread = unique_ptr<thread>(new thread(&BaseThread::run, this));
m_pthread->detach();
}
virtual ~BaseThread() = default;
void onStart() //让该子线程继续运行
{
unique_lock<mutex> lk(m_mutexStatus);
m_nStatus = RunningStatus::START;
m_cvStatus.notify_one();
}
void onPause() //让该子线程暂停运行
{
unique_lock<mutex> lk(m_mutexStatus);
m_nStatus = RunningStatus::PAUSE;
}
void onStop() //让该子线程停止、退出运行
{
unique_lock<mutex> lk(m_mutexStatus);
m_nStatus = RunningStatus::STOP;
m_cvStatus.notify_one();
}
RunningStatus getRunningStatus() //得到子线程的运行状态
{
unique_lock<mutex> lk(m_mutexStatus);
return m_nStatus;
}
protected:
void run()
{
while (RunningStatus::STOP != getRunningStatus())
{
waitIfPaused();
if (RunningStatus::START == getRunningStatus())
{
task();
}
}
cout << "Thread is stopped. " << "thread id: " << this_thread::get_id() << endl;
}
virtual void task() = 0; //实现具体的子线程业务代码
void waitIfPaused() //阻塞,直到子线程运行状态不为“暂停”状态为止
{
unique_lock<mutex> lk(m_mutexStatus);
m_cvStatus.wait(lk, [this] {return RunningStatus::PAUSE != m_nStatus; });
}
private:
unique_ptr<thread> m_pthread;
RunningStatus m_nStatus = RunningStatus::PAUSE; //子线程运行状态
mutex m_mutexStatus; //互斥锁,专门用来保护成员变量m_nStatus
condition_variable m_cvStatus; //条件变量,用来暂停时阻塞子线程
};
class Thread1 : public BaseThread {
private:
void task() override
{
cout << "Thread1 is running. " << "thread id: " << this_thread::get_id() << endl;
this_thread::sleep_for(chrono::milliseconds(1000));
}
};
class Thread2 : public BaseThread {
private:
void task() override
{
cout << "Thread2 is running. " << "thread id: " << this_thread::get_id() << endl;
this_thread::sleep_for(chrono::milliseconds(1000));
}
};
//线程管理器。为被观察者
class ThreadManager : public obs::observable<BaseThread> {
public:
void startAllthread() { //通知所有子线程开始/继续运行
notify_observers(&BaseThread::onStart);
}
void pauseAllthread() { //通知所有子线程暂停运行
notify_observers(&BaseThread::onPause);
}
void stopAllthread() { //通知所有子线程终止运行
notify_observers(&BaseThread::onStop);
}
};
int main()
{
cout << "main thread id: " << this_thread::get_id() << endl;
Thread1 thread1;
Thread2 thread2;
ThreadManager threadManager;
threadManager.add_observer(&thread1); //注册观察者
threadManager.add_observer(&thread2);
threadManager.startAllthread();
this_thread::sleep_for(chrono::milliseconds(3000));
threadManager.pauseAllthread();
this_thread::sleep_for(chrono::milliseconds(3000));
threadManager.stopAllthread();
system("pause");
return 0;
}
在主线程(线程id为116132)中创建两个子线程(线程id分别为100256,68992)。首先运行这两个子线程,3秒后暂停这两个子线程,再过3秒终止这两个子线程。效果如下:
在该例程中,让多个观察者对象(Thread1、Thread2)同时监听一个主对象(被观察者ThreadManager)。当这个主对象的状态发生变化(ThreadManager调用startAllthread() /pauseAllthread() /stopAllthread()函数)时,会通知所有观察者对象,让其更新自己(回调onStart() / onPause() / onStop() 函数)。
代码通过BaseThread中的成员变量m_nStatus来控制子线程运行状态。当让子线程暂停时,各个子线程会阻塞在函数waitIfPaused()这里(通过条件变量m_cvStatus的wait方法实现阻塞)。当让子线程终止时,也是通过标志变量m_nStatus来让子线程自己停止,这样比起暴力终止子线程可以让其资源安全地释放。