-
任务目标
(1)创建3个线程;
(2)线程1不断的生成数值数据,模拟不断读取硬件设备数据,并打印原始数据;
(3)线程2获取线程1的数据,对其进行处理1(乘以2),并打印中间结果;
(4)线程3获取线程2的处理结果,进行处理2(乘以-1),并打印最终结果;
(5)一组处理1和处理2结束后,线程2再接着获取线程1的数据,不断处理. -
几点注意的地方
(1)c++多线程用pthread还是thread
网上对thread类评价很低,但是thread用起来简单,所以这里我用thread类.
(2)线程中输出函数
printf是线程安全的,用cout在线程中输出打印内容会经常错位,因此线程中最好用printf输出.
(3)线程结束的方法
join:主线程等待子线程结束后再结束;
detach:程序将子线程看成单独的线程,子线程会在后台运行.
我们一般用join().
(4)c++类与多线程封装
要想把创建和启动线程的函数放在类中,需要静态类成员的线程函数,但是静态成员函数又无法调用非静态成员变量和方法,虽然可以七拐八拐用上,但是实现过程太繁琐,得不偿失.因此这里我只讲线程函数和锁及过程数据变量封装在类中,创建线程和启动线程放在main()函数中.
3. 文件与代码
threadclass.hpp // 线程类的定义
threadclass.cpp // 线程类的实现
main.cpp // 创建和启动线程
(1) threadclass.hpp
#ifndef THREADCLASS_THREADCLASS_HPP
#define THREADCLASS_THREADCLASS_HPP
#include <mutex>
#include <condition_variable>
using namespace std;
class ThreadClass
{
private:
int rawdata;
int data;
bool start;
bool readyfor_t2;
mutex mtx;
condition_variable cv;
public:
ThreadClass();
ThreadClass(const int x, const int y, const bool s1, const bool s2);
~ThreadClass();
void thread1(); // 生成数值并打印
void thread2(); // 数值*2并打印
void thread3(); // 在*2基础上数值*-1并打印
};
#endif //THREADCLASS_THREADCLASS_HPP
(2) threadclass.cpp
//条件变量和互斥锁实现线程同步和通信
// Created by wz on 2020/6/1.
#include "threadclass.hpp"
#include <thread>
#include <condition_variable>
using namespace std;
ThreadClass::ThreadClass()
{}
ThreadClass::~ThreadClass()
{}
ThreadClass::ThreadClass(const int x, const int y, const bool s1, const bool s2)
{
rawdata = x;
data = y;
start = s1;
readyfor_t2 = s2;
}
void ThreadClass::thread1()
{
for(int i = 0; i < 100; i++)
{
rawdata = i;
printf("生成数据: %d \n", rawdata);
start = true;
this_thread::sleep_for(std::chrono::milliseconds(1)); // 模拟读取硬件数据延时
}
start = false;
}
void ThreadClass::thread2()
{
while(start)
{
unique_lock<mutex> lock(mtx);
while(!readyfor_t2)
{
cv.wait(lock);
}
data = 2 * rawdata;
printf("处理1: %d \n", data);
this_thread::sleep_for(std::chrono::milliseconds(5)); // 模拟处理1耗费时间
readyfor_t2 = false;
cv.notify_all();
}
}
void ThreadClass::thread3()
{
while(start)
{
unique_lock<mutex> lock(mtx);
while(readyfor_t2)
{
cv.wait(lock);
}
data = -1 * data;
printf("处理2: %d \n", data);
this_thread::sleep_for(std::chrono::milliseconds(5)); // 模拟处理2耗费时间
readyfor_t2 = true;
cv.notify_all();
}
}
(3) main.cpp
#include "threadclass.hpp"
#include <iostream>
#include <thread>
using namespace std;
int main() {
cout << "主线程启动: " << endl;
ThreadClass tc{0, 0, false,true};
thread t1(&ThreadClass::thread1, &tc);
thread t2(&ThreadClass::thread2, &tc);
thread t3(&ThreadClass::thread3, &tc);
t1.join();
t2.join();
t3.join();
return 0;
}
4. 实验结果
主线程启动:
生成数据: 0
处理1: 0
生成数据: 1
生成数据: 2
生成数据: 3
生成数据: 4
处理2: 0
生成数据: 5
生成数据: 6
生成数据: 7
生成数据: 8
生成数据: 9
处理1: 18
生成数据: 10
生成数据: 11
生成数据: 12
生成数据: 13
处理2: -18
生成数据: 14
生成数据: 15
生成数据: 16
生成数据: 17
处理1: 34
生成数据: 18
生成数据: 19
生成数据: 20
生成数据: 21
生成数据: 22
处理2: -34
生成数据: 23
生成数据: 24
生成数据: 25
生成数据: 26
生成数据: 27
处理1: 54
生成数据: 28
生成数据: 29
生成数据: 30
生成数据: 31
处理2: -54
生成数据: 32
生成数据: 33
生成数据: 34
生成数据: 35
生成数据: 36
处理1: 72
生成数据: 37
生成数据: 38
生成数据: 39
生成数据: 40
处理2: -72
生成数据: 41
生成数据: 42
生成数据: 43
生成数据: 44
生成数据: 45
处理1: 90
生成数据: 46
生成数据: 47
生成数据: 48
生成数据: 49
处理2: -90
生成数据: 50
生成数据: 51
生成数据: 52
生成数据: 53
生成数据: 54
处理1: 108
生成数据: 55
生成数据: 56
生成数据: 57
生成数据: 58
生成数据: 59
处理2: -108
生成数据: 60
生成数据: 61
生成数据: 62
生成数据: 63
处理1: 126
生成数据: 64
生成数据: 65
生成数据: 66
生成数据: 67
生成数据: 68
处理2: -126
生成数据: 69
生成数据: 70
生成数据: 71
生成数据: 72
处理1: 144
生成数据: 73
生成数据: 74
生成数据: 75
生成数据: 76
生成数据: 77
处理2: -144
生成数据: 78
生成数据: 79
生成数据: 80
生成数据: 81
处理1: 162
生成数据: 82
生成数据: 83
生成数据: 84
生成数据: 85
生成数据: 86
处理2: -162
生成数据: 87
生成数据: 88
生成数据: 89
生成数据: 90
生成数据: 91
处理1: 182
生成数据: 92
生成数据: 93
生成数据: 94
生成数据: 95
处理2: -182
生成数据: 96
生成数据: 97
生成数据: 98
生成数据: 99
处理1: 198
Process finished with exit code 0
5. 小结
为了实现这个功能,花了一天时间在网上试遍了各种博客里的方法,到我这不是死锁就是乱序.最后终于结合unique互斥锁和条件变量成功了.程序中线程函数里while()的判断应该可以改成cv.wait(lock, { … });的方式.