?1. 線程共享數據會造成什么問題?
1.1 讀寫不一致
多線程讀不會造成數據變動,所以沒有問題。只要有一個線程設計修改數據,就會導致數據共享出現問題,簡單的是數據不一致,嚴重的是程序訪問已經釋放的內存,造成程序崩潰。
1.2 順序不一致(競態條件)
1.2.1 為什么要有順序性問題?
因為,讀寫操作的不同,不同的訪問順序是是十分重要的。
1.2.2? 造成順序性問題的原因?
因為,需要改動多處數據,造成必須要保證改動多出數據時每一步的連續(一個線程多處改動,多次改動,都必須搶占cpu,不能允許其他線程在這個過程被其他線程修改)
1.2.3 如何防止惡性的條件競爭?
什么是惡性競爭?可以理解為破壞程序,導致程序崩潰的,讀寫不一致程序還能運行,如果釋放指針,訪問已經釋放的地址導致內存崩潰。
防止惡性競爭的方式有:
- 修改數據時,每一步動作不被其他線程搶占。(常見的有:互斥鎖)
- 使用事務,也就是原子操作。(mysql的事務,redis中lua腳本)
2. 互斥方式保護共享數據
2.1 互斥鎖
通過std::mutex,然后調用lock和unlock函數。但是通過unlock,則需要在每一個訪問共享數據的線程上都調用unlock,同時也要注意拋出異常的問題,所以采用了基于RAII機制的lock_guard()函數,構造時加鎖,析構時釋放鎖。
缺點:一旦有暴漏的引用或者指針,那么訪問加鎖后,仍然能夠被訪問(可以推導出,加鎖并不是鎖內存,很可能就是鎖變量的訪問)。就是:你要訪問共享變量,你就需要對所有的訪問共享變量的環節都互斥鎖,進行訪問,不然的話如果通過指針或者引用去訪問內存時,是無法解決這個問題的。
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;
}data_wrapper x;void foo()
{x.process_data(malicious_function); 2.如果你某天添加功能,以引用或者指針的方式向外部調用恭喜那個變量,這個時候就是非線程安全了,因為malicious_function是非線程安全的。unprotected->do_something(); 以無保護方式訪問本應受保護的共享數據
}