环境
- wsl ubuntu 18.04 LTS
- gcc version 7.5.0
其实这个并不重要,就图个仪式感,hh。不过必须是在Linux系统下实现的,windows平台是不可以的,c++在windows平台实现多线程不是使用的这个库
时间片轮转
代码
#include <iostream>
#include <thread>
using namespace std;
void func(int i,int times){
puts("thread id: ");
for(int i=0;i<=times;i++)
printf("%d ",i);
cout<<endl;
}
int main() {
thread th[5];
for(int i=0;i<5;i++)
th[i]=thread(func,i,40);// 这里的times参数最好大一点,才能看出效果
// thread 传入的参数为:函数指针,函数的各个参数
for(int i=0;i<10;i++)
th[i].join();
return 0;
}
编译
g++ main.cpp -o main -lpthread #这里的 -lpthread 是链接thread库,很重要,不添加的话会编译不通过
这样重复运行程序,会发现每次的输出不一样。这就是不同线程时间片轮转的结果
线程同步
代码
#include <iostream>
#include <thread>
using namespace std;
int n;
void func() {
for (int i = 0; i < 10000; i++)
n++;
}
int main() {
thread th[100];
for (thread &it : th)
it = thread(func);
for (thread &it : th)
it.join();
cout << n << endl;
return 0;
}
按照逻辑应该输出 1000000
,但是实际上并没有,往往小于此值。因为各个线程出现了同时访问n
的情况,所以针对这种情况,我们需要用到互斥锁
为什么不能使用 std::atomic?
在mutex
库中常用的std::mutex
和std::atomic
都可实现互斥访问,我们常常为了追求更高的效率,会用std::atomic
而不是std::mutex
,并且std::atomic
的使用更加方便易懂,但是如果我们要用std::atomic
和std::queue
来实现消息队列,是不可行的,接下来我会根据我所找到的资料,做一个大致的解释。
-
先放一个stackoverflow上大佬的大致解释
A)
atomic <queue <T>> fifo;
B)
queue <atomic <T>> fifo;
两种方式都是不可以的,A甚至不能正确编译,B可以实现原子操作的读和写,但是无法实现
queue
的例如pop(), push()
等操作 -
首先,
atomic<T> var
中,T
的类型必须是TriviallyCopyable(翻译过来可以理解成拷贝不变)。那么是TriviallyCopyable呢?文档如下Scalar types标量类型
- 标量类型(Scalar type)是相对复合类型(Compound type)来说的:标量类型只能有一个值,而复合类型可以包含多个值。复合类型是由标量类型构成的。
- 在C语言中,整数类型(int、short、long等)、字符类型(char、wchar_t等)、枚举类型(enum)、小数类型(float、double等)、布尔类型(bool)都属于标量类型,一份标量类型的数据只能包含一个值
- 结构体(struct)、数组(array)、字符串(string)都属于复合类型,一份复合类型的数据可以包含多个标量类型的值,也可以包含其他复合类型的值。
-
rivially copyable classes, i.e. classes satisfying following requirements拷贝不变类,也就是满足以下条件:
- At least one copy constructor, move constructor, copy assignment operator, or move assignment operator is eligible必须有一个拷贝构造函数,或者移动构造函数,或者拷贝赋值运算符,或者移动赋值运算符
- Every eligible copy constructor (if any) is trivial每个复制构造函数均为平凡或弃置的
- Every eligible move constructor (if any) is trivial每个移动构造函数均为平凡或弃置的
- Every eligible copy assignment operator (if any) is trivial每个复制赋值运算符均为平凡或弃置的
- Every eligible move assignment operator (if any) is trivial每个移动赋值运算符均为平凡或弃置的
- Has a trivial non-deleted destructor平凡而未弃置的析构函数
-
Arrays of TriviallyCopyable objects可平凡复制 (TriviallyCopyable) 对象的数组
(补充:什么叫平凡类函数呢?简单点讲就是如果类T满足:1. 函数不是用户提供的,即他是隐式定义或者预置的;2. T没有虚成员函数;3. T没有虚基类;4. 为
T
的每个直接基类选择的复制构造函数都是平凡的;5. 为 T 的每个类类型(或类类型数组)的非静态成员选择的复制构造函数都是平凡的)
所以讲了一大堆,就是一个意思,用std::atomic
和std::queue
来实现消息队列,是不可行的!
实现对queue
的操作保护只能是使用mutex
!