在编程中经常需要创建子线程来执行耗时的任务,避免主线程卡死,提高程序性能。其中有很多业务场景需要我们可以自由控制子线程的开启/暂停/终止。比如音视频播放器,除了用来操作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_nStatuscondition_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来让子线程自己停止,这样比起暴力终止子线程可以让其资源安全地释放。

C++优雅地开启/暂停/停止线程——基于观察者模式相关推荐

  1. 【Java 语言】Java 多线程 一 ( 线程基础 : 线程启动 | 线程停止 | 线程暂停 | 线程优先级 | 守护线程)

    一. 线程启动 线程启动 : -- 1. 继承 Thread 运行线程 : 重写 Thread 类的 run 方法, 然后执行该线程; -- 2. 实现 Runnable 接口, 并运行线程; -- ...

  2. 停止线程 暂停线程

    停止线程 停止一个线程意味着在线程处理完任务之前停止正在进行的操作,即放弃当前的操作. Java中有以下3中方法可以终止正在运行的线程: 使用退出标志,使线程正常退出(当run方法完成后线程终止) 使 ...

  3. 【探索】停止线程和暂停线程

    停止线程和暂停线程 停止一个线程意味着在线程处理完任务之前停止正在做的操作,也就是放弃当前的操作,虽然看起来非常简单,但是要做好防范措施. 停止线程的方法 1. 方法1:使用Thread.stop() ...

  4. 开启线程_beginthreadex()和停止线程_endthreadex()

    目录 1. _beginthreadex() 开始 2. _endthreadex() 停止 3. 例子: 1. _beginthreadex() 开始 unsigned long _beginthr ...

  5. python 多线程启动多个摄像头,同时可以暂停摄像头线程

    由于比赛需要用到3个摄像头,程序是多线程架构,即主线程开三个子摄像头线程:在主线程(主循环内同时跑另外3个死循环),这样摄像头拍照非常吃cpu资源与内存,为了降低cpu的繁忙性,现考虑在需要使用某摄像 ...

  6. java 中如何正确的停止线程

    如何优雅的停止一个线程 1.为什么要停止线程 2.为何说要正确的停止线程 3.使用interrupt()停止线程 4.线程在通常三种情况下停止 4.1 普通情况 4.2 线程阻塞情况 4.3 传递中断 ...

  7. python线程暂停_在python中暂停一个线程和另一个线程

    我正在研究如何在python中执行多线程(2个线程).在 我要他们中的一个一直在读串行端口.读取每个帧并将其保存到数据库中.我已经做了一个脚本来做这个.在 对于第二个,我希望它监听一个套接字端口.当它 ...

  8. 第二十四讲 多线程——如何停止线程?

    停止线程 如何停止线程呢?须知stop()已经过时,那就只有一种方案了,即run()结束.其原理是线程任务通常都有循环,因为开启线程就是为了执行需要一些时间的代码.只要控制住循环,就可以结束run方法 ...

  9. C# Task 多线程 开始 暂停 停止

    C# Task 多线程 开始 暂停 停止 public partial class Form1 : Form{private ManualResetEvent ma;private Cancellat ...

最新文章

  1. jsp中如何显示mysql数据库数据类型_jsp中如何以表格形式显示数据库中一个字段的数据?...
  2. 用OpenCV实现图像的水平镜像(翻转)变换和竖直镜像(翻转)变换(垂直镜像变换)的源码
  3. 识别强直性脊柱炎高效和疾病特定的基质改变
  4. bzoj4316: 小C的独立集
  5. 语音通话框架_普通话考试得高分的方法
  6. Chrome调试WebView时Inspect出现空白的解决方法(使用离线包不Fan墙)
  7. 英语笔记:写作:Recreational activities
  8. ip netns的使用及network namespace 简介
  9. 用于存储过程的ASP.NET Core Blazor和EF Core原始SQL查询
  10. MyBatis使用log4j输出日志
  11. android logo:内核、android开机动画
  12. 三十而立,从零开始学ios开发(八):Autorotation and Autosizing
  13. 设计模式-结构型模式,适配器模式(4)
  14. 通过银行卡号获取银行名称
  15. java判断时间日期是否等于当前日期
  16. 了解arXiv,及arXiv的注册详细操作。
  17. 计算机专业学生进行职业决策,计算机专业学生职业生涯规划书.doc
  18. Java面试题(上)
  19. Java算法学习:蓝桥杯——地宫寻宝(DFS+动态规划—记忆型递归)
  20. 2021华为软件精英挑战赛(杭厦第20名)

热门文章

  1. idea打包springboot项目并发布到阿里云服务器
  2. 软件工程师的属性与发展
  3. Kotlin之集合排序(sortBy、sortByDescending)
  4. 数字信号处理音频FIR去噪滤波器(基于MATLAB GUI的开发)
  5. linux 开启ssh服务
  6. 棋逢对手 — XPS 15 2020 横评 MacBook Pro 16
  7. Bzoj2149拆迁队:cdq分治 凸包
  8. Linux下查看当前用户和所属用户组方法总结
  9. python的join函数的用法及实例
  10. 创建linux目录的基本命令