Parallel Tutorial 1: Data Race (Implement with C++)

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完。

發佈留言

發佈留言必須填寫的電子郵件地址不會公開。 必填欄位標示為 *