環境
- 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
!