C++ 多線程(一)
- 1.std中的thread API 介紹
- 開啟一個線程
- 獲取線程信息API
- 交換兩個線程
- 2.向線程里傳遞參數的方法
- 第一種方式(在創建線程的構造函數后攜帶參數)
- 第二種方式(Lambda)
- 第三種方式(成員函數)
- 3.互斥鎖
- 4.try_lock
- 5.掛起和喚醒線程
- 6.將主線程資源移動到其他線程
- 7.lock_guard
- 8.unique_lock
- 9.call_once
- 10.condition_variable
1.std中的thread API 介紹
開啟一個線程
如下是定義一個線程
std::thread Thread1(ThreadFunc1)
其開始運行有兩種方式,一個是阻塞當前調用進程,一個是異步的方式不會阻塞當前調用的進程
join會阻塞當前調用的進程
Thread1.join()
detach不會阻塞當前調用進程
Thread1.detach()
獲取線程信息API
獲取線程ID
Thread1.get_id()
判斷線程是否是可啟用的
Thread1.joinable()
這樣輸出的值是true
std::thread Thread1(ThreadFunc1);cout << Thread1.joinable() << endl;
如果傳入的執行參數為空則是不可啟用
std::thread Thread1;cout << Thread1.joinable() << endl;
如果想獲取計算機能創建的線程數
Thread1.hardware_concurrency()
獲取當前線程ID
std::this_thread::get_id()
使當前線程睡眠n秒
有兩種方式,一種是std的,一種是windows底層的
std::this_thread::sleep_for(chrono::seconds(2));
注意windows的睡眠需要引用頭文件Windows.h
#include <Windows.h>
Sleep(1000);
交換兩個線程
std::thread Thread1(ThreadFunc1)
std::thread Thread2(ThreadFunc2)
Thread1.spawn(Thread2)
2.向線程里傳遞參數的方法
先定義要在子線程執行的方法
void Func1(int a, const string& b)
{cout << a << endl;cout << b << endl;
}
第一種方式(在創建線程的構造函數后攜帶參數)
std::thread NewThread1(Func1, 1, "Hello");NewThread1.join();
第二種方式(Lambda)
int a = 100;std::thread NewThread2([&](int value1,const string& value2){cout << "=====================" << endl;cout << a << endl;cout << value1 << endl;cout << value2 << endl;},2,"World");NewThread2.join();
第三種方式(成員函數)
class FTestClass
{
public:void Run(int a,const string& b){cout << a << endl;cout << b << endl;}
};
FTestClass TestClass;std::thread NewThread3(&FTestClass::Run, &TestClass, 3, "TestClass");NewThread3.join();
3.互斥鎖
為了避免多線程之間的資源競爭自然需要這種互斥的鎖
使用前需要引用頭文件
#include <mutex>
mutex mx;
void NewThreadFunc()
{mx.lock();cout << "等待2s" << endl;std::this_thread::sleep_for(chrono::seconds(2));mx.unlock();
}
for (size_t i = 0; i < 5; i++){std::thread th(NewThreadFunc5);th.detach();}
最后我們會發現每隔2s輸出一次信息
我們每次使用鎖都需要lock 和 unlock 這是十分不便利的,而且如果我們忘記解鎖就會死鎖
我們可以使用析構來實現這種自動解鎖的方式
struct FEvent
{FEvent(){m.lock();}~FEvent(){m.unlock();}static mutex m;
};
mutex FEvent::m;
再使用宏包裹一下
#define LOCK_TEST FEvent LockEvent;
如下是在線程執行的函數,只需要定義一行就可以自動解鎖
void NewThreadFunc()
{LOCK_TESTcout << "等待2s" << endl;std::this_thread::sleep_for(chrono::seconds(2));
}
4.try_lock
try_lock 返回bool 值,是否能上鎖
static mutex mx;
void NewThreadFunc2()
{if (mx.try_lock()){cout << "等待2s" << endl;std::this_thread::sleep_for(chrono::seconds(2));mx.unlock();}else {cout << "鎖被使用" << endl;}
}
for (size_t i = 0; i < 5; i++){std::thread NewThread2(NewThreadFunc2);NewThread2.detach();}
最后輸出我們會發現只打印了一個等待2s 和四個鎖被使用
5.掛起和喚醒線程
定義在子線程執行的函數
void NewThreadFunc3()
{while (true){Sleep(1000);cout << "Hello" << endl;}
}
SuspendThread 用于掛起線程
ResumeThread 用于喚醒線程
std::thread th(NewThreadFunc3);SuspendThread(th.native_handle());std::this_thread::sleep_for(chrono::seconds(2));ResumeThread(th.native_handle());
6.將主線程資源移動到其他線程
定義在子線程執行的函數
void NewThreadFunc4(const string& Value)
{cout << Value << endl;
}
std::thread th1(NewThreadFunc4,move("Hello world"));std::thread th2 = move(th1);th2.detach();
將th1的所有權轉移給th2。此時,th1不再代表任何線程(相當于th1處于“空”狀態),而th2現在代表原來th1所代表的線程。這是因為std::thread是不可復制的,但可以移動。所以,這里通過移動賦值(或移動構造)將th1的線程所有權轉移給th2。
7.lock_guard
類似我們之前自己封裝的自動解鎖的鎖,不要手動調用unlock,函數執行完畢自動析構
mutex mx;
void NewThreadFunc5()
{lock_guard<mutex> lock(mx);cout << "Hello world" << endl;Sleep(2000);
}
for (size_t i = 0; i < 5; i++){std::thread th(NewThreadFunc5);th.detach();}
每隔2s輸出一次一共輸出了5次
8.unique_lock
unique_lock 相對于上面的 lock_guard 多了更多的功能API 可以更自由豐富的操作鎖
defer_lock 此參數是延時的意思,鎖并不是立馬生效,需要手動lock
unique_lock 也是過了作用域會自動解鎖
static mutex mt;
void Func1()
{unique_lock<mutex> lock1(mt,defer_lock);// 不需要上鎖的函數cout << "這是不需要上鎖的函數" << endl;lock1.lock();cout << "這是需要上鎖的函數" << endl;lock1.unlock();//lock2.try_lock_until(); 鎖到某個時間點//lock2.release(); 釋放//lock2.mutex();拿到鎖本體//lock2.swap(); 交換//lock2.owns_lock(); 判斷是否鎖住
}
下面演示一個unique_lock 的 try_lock_for()方法
static timed_mutex timeMt;
void Func2()
{unique_lock<timed_mutex> lock2(timeMt, defer_lock);if (lock2.try_lock_for(chrono::seconds(2))){cout << "鎖住2s后" << endl;this_thread::sleep_for(chrono::seconds(1));}else{cout << "鎖正在被占用" << endl;}
}
for (size_t i = 0; i < 5; i++){thread th(Func2);th.detach();}
我們會發現輸出了2個鎖住2s后和3個鎖正在被占用,是因為這個鎖定義了鎖住的時間為2s,當第一次運行后只執行了1s第二次進入仍然沒超過定義的鎖的2s所以可以進入,而之后都超過2s了,故無法進入了。
9.call_once
顧名思義就是無論調用多少次只執行一次
once_flag oneFlag;
void Func3()
{call_once(oneFlag, [](){cout << "運行一次" << endl;});
}
for (size_t i = 0; i < 5; i++){Func3();}
10.condition_variable
條件變量,一個地方可以等待直到通知這個等待就可以執行等待之后的代碼
mutex tx1;
condition_variable cv;
void Func4()
{Sleep(1000);cv.notify_one();Sleep(1000);
}
std::thread th(Func4);th.detach();unique_lock<mutex> Lock1(tx1);cv.wait(Lock1);cout << "運行Wait之后" << endl;
notify_one 是一個,還有多個版本notify_all