C++多執行緒庫筆記1
阿新 • • 發佈:2018-12-19
來源:C++ 11 多執行緒
課程介紹
併發:同一時間間隔
並行:同一時刻
多程序:程序間通訊:檔案、管道、訊息佇列
多程序:共享記憶體
C++多執行緒庫<thread>
建立一個執行緒
thread t(callable);
其中callable
為可呼叫物件
void greeting() {
cout << "hello multithread" << endl;
}
int main() {
thread t(greeting);
t.join();
return 0;
}
t.join()
表示主執行緒等待子執行緒結束後執行
如果主執行緒不等待子執行緒結束,使用detach
void greeting() {
cout << "hello multithread" << endl;
}
int main() {
thread t(greeting);
t.detach();
return 0;
}
由於主執行緒執行太快,子執行緒還沒有執行,主執行緒就已經退出
不能同時對一個執行緒呼叫detach()
和join()
void greeting() {
cout << "hello multithread" << endl;
}
int main () {
thread t(greeting);
t.join();
t.detach();
return 0;
}
可以使用函式判斷能否join
int main() {
thread t(greeting);
if (t.joinable())
t.join();
return 0;
}
執行緒管理
// 考慮下面的例子
void greeting() {
cout << "hello multithread" << endl;
}
int main() {
thread t(greeting);
for(int i=0;i< 100;++i){
cout<<"from main: "<< i <<endl;
}
t.join();
return 0;
}
上面的程式碼,如果for迴圈中丟擲異常,在join
前t
就會被銷燬,可以修改為
void greeting() {
cout << "hello multithread" << endl;
}
int main() {
thread t(greeting);
try {
for (int i = 0; i<100; ++i) {
cout << "from main: " << i << endl;
}
}
catch (...) {
t.join();
throw;
}
t.join();
return 0;
}
這樣,即使丟擲了異常,也會呼叫join
可以通過可呼叫物件建立執行緒
public:
void operator()(string& msg) {
for (int i = 0; i < 1; ++i)
cout << "from t1: " << msg << endl;
msg = "I love HouZongliang";
}
};
int main() {
string s = "I love Houzi";
thread t((Fator()), s);
t.join();
cout << s << endl;
return 0;
}
上面的形參是引用,但我們發現,儘管修改了msg,但是實參並沒有被修改。如果要修改引數的話,需要這樣傳參。
class Fator {
public:
void operator()(string& msg) {
for (int i = 0; i < 1; ++i)
cout << "from t1: " << msg << endl;
msg = "I love Cpp";
}
};
int main() {
string s = "I love Houzi";
thread t((Fator()), ref(s));
t.join();
cout << s << endl;
return 0;
}
如果主執行緒中不需要再使用s,可以用move把s變成右值
class Fator {
public:
void operator()(string& msg) {
for (int i = 0; i < 1; ++i)
cout << "from t1: " << msg << endl;
msg = "I love Cpp";
}
};
int main() {
string s = "I love Houzi";
thread t((Fator()), move(s));
t.join();
cout << s << endl;
return 0;
}
可以發現s已經是空值了。
標準庫中有很多不能複製,只能移動的型別,比如thread
//下面的會報錯
//thread t1 = t;
thread t1 = move(t);
下面的函式給出了最大可建立的執行緒數
int main() {
//string s = "I love Houzi";
//thread t((Fator()), move(s));
//t.join();
cout << std::thread::hardware_concurrency() << endl;
return 0;
}
資料競爭與互斥物件
下面的程式碼,打印出來的結果往往是亂的
void greeting() {
for(int i=100;i>0;--i)
std::cout << "from t1: "<< i << std::endl;
return;
}
int main() {
thread t(greeting);
for (int i = 100; i>0; --i)
std::cout << "from main" << i << std::endl;
t.join();
return 0;
}
原因是不同的執行緒競爭資源cout。
可以使用互斥物件來同步資源的訪問。
#include <mutex>
std::mutex mu;
void shared_print(string msg, int id) {
mu.lock();
cout << msg << " " << id << endl;
mu.unlock();
}
void greeting() {
for(int i=100;i>0;--i)
shared_print("from t1: ", i);
return;
}
int main() {
thread t(greeting);
for (int i = 100; i>0; --i)
shared_print("from main", i);
t.join();
return 0;
}
但是cout << msg << " " << id << endl;
可能會丟擲異常。使用類lock_guard<mutex>
來管理互斥鎖
void shared_print(string msg, int id) {
//mu.lock();
lock_guard<mutex> guard(mu);
cout << msg << " " << id << endl;
//mu.unlock();
}
當guard
析構的時候,鎖會被釋放。
這個執行緒還有一個問題,cout是一個全域性的變數。其他函式可以在不加鎖的情況下使用cout。
class LofFile {
mutex m_mutex;
ofstream f;
public:
LofFile() {
f.open("log.txt");
}
void shared_print(string id, int val) {
lock_guard<mutex> guard(m_mutex);
f << "From " << id << ": " << val << endl;
}
};
void greeting(LofFile& log) {
for(int i=100;i>0;--i)
log.shared_print("from t1: ", i);
return;
}
int main() {
LofFile log;
thread t(greeting,ref(log));
for (int i = 100; i>0; --i)
log.shared_print("from main", i);
t.join();
return 0;
}
上面的例子中,f始終在mutex的保護之內,但是我們不能把f返回給類外,或者把f傳給其他引數,比如
ofstream& GetStream() { return f; }
或者
void processf(void fun(ofstream&)){
fun(f);
}
死鎖
假設有下面的程式碼
class LofFile {
mutex m_mutex;
mutex m_mutex2;
ofstream f;
public:
LofFile() {
f.open("log.txt");
}
void shared_print(string id, int val) {
lock_guard<mutex> guard(m_mutex);
lock_guard<mutex> guard2(m_mutex2);
cout << "From " << id << ": " << val << endl;
}
void shared_print2(string id, int val) {
lock_guard<mutex> guard2(m_mutex2);
lock_guard<mutex> guard(m_mutex);
cout << "From " << id << ": " << val << endl;
}
};
void greeting(LofFile& log) {
for(int i=100;i>0;--i)
log.shared_print("t1: ", i);
return;
}
int main() {
LofFile log;
thread t(greeting,ref(log));
for (int i = 100; i>0; --i)
log.shared_print2("main", i);
t.join();
return 0;
}
執行結果:
避免方式:
- 申請資源順序相同
class LofFile {
mutex m_mutex;
mutex m_mutex2;
ofstream f;
public:
LofFile() {
f.open("log.txt");
}
void shared_print(string id, int val) {
lock_guard<mutex> guard(m_mutex);
lock_guard<mutex> guard2(m_mutex2);
cout << "From " << id << ": " << val << endl;
}
void shared_print2(string id, int val) {
lock_guard<mutex> guard(m_mutex);
lock_guard<mutex> guard2(m_mutex2);
cout << "From " << id << ": " << val << endl;
}
};
void greeting(LofFile& log) {
for(int i=100;i>0;--i)
log.shared_print("t1: ", i);
return;
}
int main() {
LofFile log;
thread t(greeting,ref(log));
for (int i = 100; i>0; --i)
log.shared_print2("main", i);
t.join();
return 0;
}
- 使用C++提供的lock
此時adopt_lock是為了告訴lock_guard已經鎖了,只需要獲取試用權,並釋放
class LofFile {
mutex m_mutex;
mutex m_mutex2;
ofstream f;
public:
LofFile() {
f.open("log.txt");
}
void shared_print(string id, int val) {
lock(m_mutex, m_mutex2);
lock_guard<mutex> guard(m_mutex,std::adopt_lock);
lock_guard<mutex> guard2(m_mutex2, std::adopt_lock);
cout << "From " << id << ": " << val << endl;
}
void shared_print2(string id, int val) {
lock(m_mutex, m_mutex2);
lock_guard<mutex> guard2(m_mutex2, std::adopt_lock);
lock_guard<mutex> guard(m_mutex, std::adopt_lock);
cout << "From " << id << ": " << val << endl;
}
};
void greeting(LofFile& log) {
for(int i=100;i>0;--i)
log.shared_print("t1: ", i);
return;
}
int main() {
LofFile log;
thread t(greeting,ref(log));
for (int i = 100; i>0; --i)
log.shared_print2("main", i);
t.join();
return 0;
}
- 避免使用多個鎖
Unique Lock和Lazy Initialization
另一種加鎖的方式
class LofFile {
mutex m_mutex;
ofstream f;
public:
LofFile() {
f.open("log.txt");
}
void shared_print(string id, int val) {
std::unique_lock<mutex> locker(m_mutex);
cout << "From " << id << ": " << val << endl;
locker.unlock();
//後面的操作不需要加鎖
}
};
void greeting(LofFile& log) {
for(int i=100;i>0;--i)
log.shared_print("t1: ", i);
return;
}
int main() {
LofFile log;
thread t(greeting,ref(log));
for (int i = 100; i>0; --i)
log.shared_print("main", i);
t.join();
return 0;
}
unique_lock還有其他靈活的操作
class LofFile {
mutex m_mutex;
ofstream f;
public:
LofFile() {
f.open("log.txt");
}
void shared_print(string id, int val) {
std::unique_lock<mutex> locker(m_mutex,std::defer_lock);
//...目前還沒有加鎖
locker.lock();
cout << "From " << id << ": " << val << endl;
locker.unlock();
//後面的操作不需要加鎖
}
};
void greeting(LofFile& log)