Data Race是指兩個或以上的thread在修改同一個數據時發生衝突,造成修改後的數據不正確。
Data Race Case
#include <thread>
unsigned int project_count =0;
void adder()
{
for(int i=0;i<1000000;i++)
project_count++;
}
int main()
{
std::thread tim(adder);
std::thread jason(adder);
tim.join();
jason.join();
printf("We should finish %u projects.\n", project_count);
}
以上的Code會產生data race的問題,最後出來的project數量並不會是 2000000,而且每次出現的數量會不同。
To Avoid The Data Race
#include <thread>
#include <mutex>
unsigned int project_count =0;
std::mutex pm;
void adder()
{
pm.lock();
for(int i=0;i<10;i++)
{
project_count++;
}
pm.unlock();
}
int main()
{
std::thread tim(adder);
std::thread jason(adder);
tim.join();
jason.join();
printf("We should finish %u projects.\n", project_count);
}
為了避免產生thread同時讀取以及修改共同數據時,造成得修改時間落差,我們使用mutex來保護他的共同資源,當其中一個thread在讀取共同資源時,他需要先將mutex上鎖,一直到他完成對該資源的任務時,他再釋放mutex。
mutex 放置的位置
// Case A
#include <thread>
#include <mutex>
#include <Windows.h>
#include <chrono>
unsigned int project_count =0;
std::mutex pm;
void adder()
{
for(int i=0;i<10;i++)
{
std::this_thread::sleep_for(std::chrono::microseconds(500));
pm.lock();
project_count++;
pm.unlock();
}
}
int main()
{
LARGE_INTEGER t1, t2, tf;
QueryPerformanceFrequency(&tf);
QueryPerformanceCounter(&t1);
std::thread tim(adder);
std::thread jason(adder);
tim.join();
jason.join();
printf("We should finish %u projects.\n", project_count);
QueryPerformanceCounter(&t2);
double time = ((double)t2.QuadPart-(double)t1.QuadPart)/(double)tf.QuadPart*1000.0;
printf("time = %f ms\n", time);
}
// Case B
#include <thread>
#include <mutex>
#include <Windows.h>
#include <chrono>
unsigned int project_count =0;
std::mutex pm;
void adder()
{
pm.lock();
for(int i=0;i<10;i++)
{
std::this_thread::sleep_for(std::chrono::microseconds(500));
project_count++;
}
pm.unlock();
}
int main()
{
LARGE_INTEGER t1, t2, tf;
QueryPerformanceFrequency(&tf);
QueryPerformanceCounter(&t1);
std::thread tim(adder);
std::thread jason(adder);
tim.join();
jason.join();
printf("We should finish %u projects.\n", project_count);
QueryPerformanceCounter(&t2);
double time = ((double)t2.QuadPart-(double)t1.QuadPart)/(double)tf.QuadPart*1000.0;
printf("time = %f ms\n", time);
}
Case A所需的時間為24 ms,然而Case B花的時間為322ms (Debug mode),Case A只有在對資源要進行操作的時候進行了mutex的保護,但Case B在非進行資源操作的時候(sleep的時候),仍然鎖著,如此一來沒法讓另一個thread事先做事,其他thread必須要等該thread sleep完。