C++11 多執行緒執行緒共享資料
阿新 • • 發佈:2018-11-19
共享資料的問題
這些在作業系統中都有詳細的介紹,可以回顧作業系統課程。。很典型的就是資料競爭問題。
互斥量保護資料
最原始的方式:使用std::mutex
建立互斥量,使用成員lock()
加鎖,使用成員unlock()
解鎖。但是這種方式需要我們在每個函數出口都呼叫一次unlock()
,過於繁瑣。。。
例項:
// std::lock example
#include <iostream> // std::cout
#include <thread> // std::thread
#include <mutex> // std::mutex, std::lock
std::mutex foo,bar; // 全域性鎖
void task_a () {
// foo.lock(); bar.lock(); // replaced by:
std::lock (foo,bar);
std::cout << "task a\n";
foo.unlock();
bar.unlock();
}
void task_b () {
// bar.lock(); foo.lock(); // replaced by:
std::lock (bar,foo);
std::cout << "task b\n";
bar.unlock();
foo.unlock();
}
int main ()
{
std::thread th1 (task_a);
std::thread th2 (task_b);
th1.join();
th2.join();
return 0;
}
/*
輸出結果:
task a
task b
*/
一般使用std::lock_guard
模板類,在構造的時候對互斥量加鎖,析構時解鎖。
#include <iostream>
#include <mutex>
#include <thread>
#include <stdexcept>
std::mutex mtx;
void print_even(int x) {
if(x % 2 == 0)
std::cout << x << " is even\n";
else
throw (std::logic_error("not even"));
}
void print_thread_id(int id) {
try {
std::lock_guard<std::mutex>lck(mtx); // 函式結束時,自動析構解鎖
print_even(id); // 相當於在這裡加鎖了
} catch(std::logic_error&) {
std::cout << "[exception caught]\n";
}
}
int main() {
std::thread threads[10];
for(int i = 0; i < 10; ++i) {
threads[i] = std::thread(print_thread_id, i + 1);
}
for(auto& th : threads)
th.join();
}
預防死鎖
一般都是程式設計時候進行考慮的。。。方法留作後續補充。。作業系統上的思想為指導。
注意合理組織程式碼
C++中,如果是直接對地址進行操作,那麼鎖無法阻止這種行為。
class some_data
{
int a;
std::string b;
public:
void do_something();
};
class data_wrapper
{
private:
some_data data;
std::mutex m;
public:
template<typename Function>
void process_data(Function func)
{
std::lock_guard<std::mutex> l(m);
func(data); // 1 傳遞“保護”資料給使用者函式
}
};
some_data* unprotected;
void malicious_function(some_data& protected_data)
{
unprotected=&protected_data; // 這裡直接獲取地址,那麼unprotected的操作無法被鎖限制
}
data_wrapper x;
void foo()
{
x.process_data(malicious_function); // 2 傳遞一個惡意函式
unprotected->do_something(); // 3 在無保護的情況下訪問保護資料
}
因此程式碼中儘量避免直接對臨界區的資料進行地址操作。