c++ 11 多线线程系列-----thread

一、与 C++11 多线程相关的头文件地方

C++11 新标准中引入了四个头文件来支持多线程编程,他们分别是<atomic> ,<thread>,<mutex>,<condition_variable>和<future>。

  • <atomic>:该头文主要声明了两个类, std::atomic 和 std::atomic_flag,另外还声明了一套 C 风格的原子类型和与 C 兼容的原子操作的函数。
  • <thread>:该头文件主要声明了 std::thread 类,另外 std::this_thread 命名空间也在该头文件中。
  • <mutex>:该头文件主要声明了与互斥量(mutex)相关的类,包括 std::mutex 系列类,std::lock_guard, std::unique_lock, 以及其他的类型和函数。
  • <condition_variable>:该头文件主要声明了与条件变量相关的类,包括 std::condition_variable 和 std::condition_variable_any。
  • <future>:该头文件主要声明了 std::promise, std::package_task 两个 Provider 类,以及 std::future 和 std::shared_future 两个 Future 类,另外还有一些与之相关的类型和函数,std::async() 函数就声明在此头文件中。
本篇博文研究的是thread头文件中的类:

我们来看第一个利用c++11特性创建线程的例子:
        
#include <iostream>
#include <thread>

using namespace std;

void th_function()
{
	std::cout << "hello thread." << std::endl;
}

int main(int argc, char *argv[])
{
	std::thread t(th_function);
	t.join();

	return 0;
}



二、现在我们来看看c++ 11中thread头文件中的thread类:


三、std::thread 构造

default (1)
thread() noexcept;
initialization (2)
template <class Fn, class... Args>
explicit thread (Fn&& fn, Args&&... args);
copy [deleted] (3)
thread (const thread&) = delete;
move (4)
thread (thread&& x) noexcept;

(1). 默认构造函数,创建一个空的 thread 执行对象。
(2). 初始化构造函数,创建一个 thread对象,该 thread对象可被 joinable,新产生的线程会调用 fn 函数,该函数的参数由 args 给出。
(3). 拷贝构造函数(被禁用),意味着 thread 不可被拷贝构造。
(4). move 构造函数,move 构造函数,调用成功之后 x 不代表任何 thread 执行对象。
注意:可被 joinable 的 thread 对象必须在他们销毁之前被主线程 join 或者将其设置为 detached.

看一个例子:
   这个例子中创建了两个线程,其中一个线程函数带参数,大家仔细分析上面的thread类的构函数,看如何创建线程。
// thread example
#include <iostream>       // std::cout
#include <thread>         // std::thread

void thr_function1()
{
	for (int i = 0; i != 10; ++i)
	{
		std::cout << "thread 1 print " << i << std::endl;
	}
}

void thr_function2(int n)
{
	std::cout << "thread 1 print " << n << std::endl;
}

int main()
{
	std::thread t1(thr_function1);     // spawn new thread that calls foo()
	std::thread t2(thr_function2, 111);  // spawn new thread that calls bar(0)

	std::cout << "main, foo and bar now execute concurrently...\n";

	// synchronize threads:
	t1.join();                // pauses until first finishes
	t2.join();               // pauses until second finishes

	std::cout << "thread 1 and htread 2 completed.\n";

	return 0;
}


大家的输出可能不是这样,有可能是下面这样的,不要紧,因为这个例子中没使用多线程的异步机制,线程之间存在竞争,看稍后文章讲解所以输出可能是下面这样的情况。



三、move 赋值操作

move (1)
thread& operator= (thread&& rhs) noexcept;
copy [deleted] (2)
thread& operator= (const thread&) = delete;
  • (1). move 赋值操作,如果当前对象不可 joinable,需要传递一个右值引用(rhs)给 move 赋值操作;如果当前对象可被 joinable,则 terminate() 报错。
  • (2). 拷贝赋值操作被禁用,thread 对象不可被拷贝。
看下面的例子。

// example for thread::operator=
#include <iostream>       // std::cout
#include <thread>         // std::thread, std::this_thread::sleep_for
#include <chrono>         // std::chrono::seconds

void pause_thread(int n)
{
	std::this_thread::sleep_for(std::chrono::seconds(n));//c++11 this_thread 类
	std::cout << "pause of " << n << " seconds ended\n";
}

int main()
{
	std::thread threads[5];                         // default-constructed threads

	std::cout << "Spawning 5 threads...\n";
	for (int i = 0; i<5; ++i)
		threads[i] = std::thread(pause_thread, i + 1);   // move-assign threads 这里调用move复制函数

	std::cout << "Done spawning threads. Now waiting for them to join:\n";
	for (int i = 0; i<5; ++i)
		threads[i].join();

	std::cout << "All threads joined!\n";

	return 0;
}



参考资料:http://www.cplusplus.com/reference/thread/thread/
                    http://www.cnblogs.com/haippy/p/3236136.html


                

  • 2
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
【RT-Thread作品秀】家庭医用多功能检测仪作者:李元江 概述背景随着人们生活水平不断提高,人们越来越注重身心健康。现在家用型的医用检查仪器太少,很多人想要检查一些简单的项目,也要专门去医院检查,这样很不方便。普通的家用医用型检查仪器,功能简单,缺少智能性。所以智能家庭医用多功能检测仪器是很有需要的。 功能实时时间显示 网络时间获取、自动对时 天气情况获取、显示 显示室内温度、气压 检测心率、血氧浓度,检测时可以实时查看心率变化值。 开发环境硬件:APT-Pi V1.5 、ESP32-01S 、LPS22HH 、MAX30102 、4.3寸触摸屏(480*272) RT-Thread版本:V4.0.2 开发工具及版本:RT_Thread StudioV1.1.5 RT-Thread使用情况概述内核部分: 任务调度,本软件设计五个执行线程,分别为Main线程、RTC线程、LPS22HH线程、MAX30102线程、TouchGFX线程。 组件部分: Finsh、C++、libc、i2c框架。 C++,i2c在移植TouchGFX需要使用到。 软件包部分: cJson、gt9147 通过cJson解析从网络上获取到的天气数据。 触摸屏触摸芯片为gt9147,所以需要gt9147软件包。 库部分: TouchGFX库 GUI界面显示框架。 硬件框架该设计硬件大体有五大部分:主控、触摸屏、wifi模块、温度传感器、心率血氧传感器。它们的连接框图如下图所示。 主控板使用的是ART_Pi。 触摸屏使用RGB888和I2C接口与主控板进行连接,触摸屏芯片为GT9147。 温度传感器使用的是LSP22HH,LSP22HH是一款兼容IIC和SPI接口的芯片,能够采集温度和气压值,这里使用使用SPI与主控板进行通信。 心率血氧传感器使用的是MAX30102,主要通过IIC接口与主控板进行通信,INT中断输出引脚与主控GPIO进行连接,主控可以根据INT信号,判断数据是否可读。 Wifi模块使用的是ESP32-01S,ART_Pi自带有Wifi模块但是最近没有时间去研究该自带模块该怎么去使用,所以先使用ESP32-01S进行开发。ESP32-01S主要使用UART串口与主控板进行通信。主控板的两个GPIO口与ESP32-01S的CH_PD和RST连接。CH_PD是ESP32-01S的使能信号,RST是重启信号。 软件框架说明本设计使用RT_Thread进行开发,主要设计有五个执行线程,分别为Main线程、RTC线程、LPS22HH线程、MAX30102线程、TouchGFX线程。 总的软件框图如下图所示。 系统开启后,主要进行RT_Thread系统内部初始化,然后进行各个线程的调用。 软件模块说明Main线程进入Main线程后,会先通过Wifi模块获取网络时间和天气情况。这就相当于系统启动后,会先获取网络时间和天气情况,到达开机自动对时功能。之后会进入while(1)循环,在While循环中根据RefreshFlag标志位,来判断是否手动获取网络时间和天气数据。在触摸屏主界面点击刷新按钮,会置位RefreshFlag标志位,从而手动获取网络时间个天气数据。 其流程图如下所示。 RTC线程我这里没使用RT_Thread的RTC,而是自己实现了一个RTC。线程设置为每1S进入该线程,进入后,时间戳数据+1,然后通过把时间戳转化为北京时间,就可以得到时间数据。当然,这里如果长时间运行,时间偏差肯定会大,考虑到设备一般不会长时间使用,所以暂时使用该方案。 LPS22HH线程LPS22HH线程任务主要是获取温度和气压情况。刚开始进入该线程时,先进行SPI的初始化和LPS22HH传感器的初始化,然后每2S调度一次进行获取温度和气压值。 其流程图如下所示。 MAX30102线程MAX30102线程任务主要是获取血氧和心率值。刚进入该线程时,先进行IIC的初始化和MAX30102器件初始化。然后进入一个循环调度过程,在循环调度过程中,根据checkFlag标志位状态,来判断是否进行获取血氧和心率值。CheckFlag可以在触摸屏检测界面进行置位和清零。点击第一次checkFlag置位,点击第二次chechFlag清零。 其流程图如下所示。 TouchGFX线程触摸屏GUI方面用的TouchGFX框架。为TouchGFX设置了一个循环调度线程。该线程主要包括两个任务处理,显示处理和触摸处理。显示处理主要是更新时间、温度气压情况、血氧、心率值和心率曲线值。触摸处理主要是进行界面更换,获取网络时间、实时天气情况,心率、血氧检测操作。 其流程图如下所示。 GUI界面GUI使用TouchGFX框架,主要设计有两个界面:主界面和检测界面。 主界面 主界面主要功能是显示时间、
概述: 冬天到了,又到了靠烧煤取暖的季节了,虽然我们大南方还不知道啥时候才能享受到北方小伙伴一样的集中供暖,但是冬天的霾却不分彼此的席卷了全国各地。本方案使用的是RT-Thread的嵌入式实时系统,通过PMS70003传感器采集空气中的PM2.5浓度数据,并通过WiFi传输到移动onenet平台上,可以远程查看PM2.5实时数据。 开发环境: RT-Thread版本: 4.0.3 开发工具:使用RT-Thread Studio2.0.0 硬件平台:基于RT-Thread官方ART-Pi开发板+自制传感器拓展板 RT-Thread使用情况概述: 本项目基于RT-Thread内核版本: Ver 4.0.3开发,使用RT-Thread Studio V2.0.0开发,使用的软件包包括cjson、onenet、webclient、pahomqtt、fal、easyflash、u8g2; 打开的组件和服务层有:finsh命令、DFS、ulog日志、C++、lwIP、POSIX、libc、ymodem,以及串口、Pin、SPI、SFUD、SDIO。 硬件框架: 本项目使用的是官方的Art-Pi开发板+自己做的传感器拓展板制作的,拓展板上包含了一个SHT20的温湿度传感器,一个攀藤的PMS7003PM2.5传感器、一个炜盛的MQ-4B半导体可燃气体传感器、一个0.91”Oled单色显示屏,以及一个拨盘按键。其中PMS7003是通过串口把采集到的数据传输到ART-PI上,由板载WiFi连接到移动onenet云上实现数据上传。SHT20和OLED共用一组IIC数据总线,可燃气体传感器占用了两个ADC口(一组检测气体浓度,一组检测传感器失效),并且可以通过背面跳线选择ADC口。按键占用了额外的三个GPIO用于设定一些配置信息。 软件框架说明: 首先RT-Thread启动后,对各部分外设进行初始化,然后进入pms7003的读取线程,该线程负责采集PMS7003上传上来的PM2.5浓度数据,并在线程里对数据进行校验求解后,通过onenet线程,我们把采集到的数据通过WiFi定时推送到移动onenet云平台,推送频率为2S/次。通过后台的数据流信息页面,可以看到实时的浓度数据信息。 软件模块说明: 软件采用的RTT4.0.3为内核基础,使用了一个独立线程来读取PMS传感器的数据,一个onenet软件包来实现联网推送的服务,最后由板载的WiFi模块负责连接到网络并把数据推送上去。 演示效果: 附上视频效果: 比赛感悟: 在使用rt-thread实时系统以前,我一直都是简单的使用裸机开发,纯粹的单流水线模式,经常会因为各种时间任务分配问题搞得头大,每种硬件使用起来得先仔细了解数据手册,了解器件性质,功能,然后才敢上板子,撸代码。 这次其实也是我第一次接触嵌入式实时操作系统这么一个方式来开发单片机,讲真,里面走了不少的弯路,也有很多没遇到过的坑。不得不说,RT-Thread的文档支持工作做的还是非常到位的,每个细节、功能都做了详细的介绍,但是诚如此还是有一部分我没太想明白的地方,这里就多亏了官方的比赛群以及我的小伙伴们,遇到很多事情找他们帮忙都能很快的解决掉。 本次比赛我还是学到了很多的新鲜知识,而且RTT的使用的便捷性给我了很大的震撼,有许多代码例程写的都很经典,后面板子上的几个设备我还要继续调试下去,争取把这个项目继续走下去,把rtt学得更深入一些。希望以后也能自己独立开发一些项目。把自己平时那么多奇奇怪怪的小点子都实现出来。
1. 创建一个基于对话框的应用程序。并增加如图所示控件;分别为3个进度条控件关联三个进度条型的变量;并在对话框的初始化函数中,设定进度条的范围;为编辑框关联一个整型的变量;为12个按钮添加消息处理函数; 2. 定义结构体:用做线程函数的参数传递 typedef struct Threadinfo{ CProgressCtrl *progress;//进度条对象 int speed; //进度条速度 int pos; //进度条位置 } thread,*lpthread; 3. 为对话框增加三个句柄,用于标识各个线程; HANDLE hThread1; //线程1线程句柄 HANDLE hThread2; //线程2线程句柄 HANDLE hThread3; //线程3线程句柄 在增加三个结构体型的变量,用做线程函数的参数传递; HANDLE hThread1; //线程1线程句柄 HANDLE hThread2; //线程2线程句柄 HANDLE hThread3; //线程3线程句柄 4. 新增一个静态的全局变量,用于记录所有线程的状态:static int GlobalVar=10000; 5. 声明并编写线程函数,注意只能有一个参数,且函数的返回值型也是固定的;函数名可以自定义; DWORD WINAPI ThreadFun(LPVOID pthread);//线程入口函数 6. 在启动按钮的消息处理函数中编写如下代码: thread1.progress=&m_progress1;//进度条对象 thread1.speed=100;//速度 thread1.pos=0;//初始位置 hThread1=CreateThread(NULL,0,ThreadFun,&thread1;,0,0);//创建并开始线程 if (!hThread1) { MessageBox("创建线程失败"); } 7. 编写线程函数(一般是一个死循环,或者需要花费时间很长的算法!否者就失去了多线程的意义) DWORD WINAPI ThreadFun(LPVOID pthread) //线程入口函数 { lpthread temp=(lpthread)pthread;//参数强制转换为结构体型 temp->progress->SetPos(temp->pos); //设置被传递过来的进度条的位置 while(temp->posspeed); /设置速度 temp->pos++; //增加进度 temp->progress->SetPos(temp->pos); //设置进度条的新位置 GlobalVar--; if(temp->pos==20) { temp->pos=0; //进度条满则归0 } } return true; } 8. 在挂起按钮函数中,编写如下代码: if(SuspendThread(hThread1)==0xFFFFFFFF) { MessageBox("挂起失败!进程可能已经死亡或未创建!"); return; } 9. 在执行按钮函数中,编写如下代码: if(ResumeThread(hThread1)==0xFFFFFFFF) { MessageBox("执行失败!进程可能已经死亡或未创建!"); return; } 10. 在停止按钮函数中,编写如下代码: if(TerminateThread(hThread1,0))//前些终止线程 { CloseHandle(hThread1);//销毁线程句柄 } else { MessageBox("终止进程失败!"); } 11. 为应用程序添加WM_TIMER消息,实时更新全局变量的值到编辑框;

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值