环境

  • 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::mutexstd::atomic都可实现互斥访问,我们常常为了追求更高的效率,会用std::atomic而不是std::mutex,并且std::atomic的使用更加方便易懂,但是如果我们要用std::atomicstd::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::atomicstd::queue来实现消息队列,是不可行的!

实现对queue的操作保护只能是使用mutex!