我們的任務涉及創建一個排序演算法,該演算法對隨機生成的整數陣列進行排序。執行程式時設定為引數的陣列大小。
為了測驗,我們列印已排序陣列的前 10 個元素以及執行時間。
當我們不在生成隨機陣列的函式中插入多執行緒時,我們的實作可以正常作業。然而,當使用并行代碼時,在大約 10% 的情況下,我們會得到意想不到的結果。以 12000000 大小的陣列為例:
第一次執行輸出:0 1 2 2 4 7 7 9 9 9
第二次執行輸出:0 1 1 1 1 2 4 4 7 7
第三次執行輸出:0 1 1 1 1 2 2 2 4 4
第 4 次執行輸出:0 1 1 2 4 7 7 9 12 16
第5次執行輸出:0 10278907 1671508 1716191 145377 3825599 1265238 859463 6112391 11065992
第 n 次執行輸出:更多的預期結果和偶爾的意外結果。
起初,我認為問題在于我們使用的 rand() 函式不是執行緒安全的。所以我改變了我們的功能:
void randomizeArray(int* array, int size, int max_value) {
int i;
#pragma omp parallel for
for (i = 0; i < size; i ) {
array[i] = rand() % max_value;
}
}
對此:
void randomizeArray(int* array, int size, int max_value) {
int i;
unsigned int seed = 1;
#pragma omp parallel for
for (i = 0; i < size; i ) {
array[i] = rand_r(&seed) % max_value;
}
}
結果是一樣的。一堆正確排序的 1-2 位輸出和偶爾未排序的大整數陣列。這與隨機化功能有關嗎?或者它可能是別的東西?
先感謝您。
uj5u.com熱心網友回復:
您是對的,rand不能保證該函式是執行緒安全的,rand_r應該使用該函式。
但是,您的替換實作也不是執行緒安全的。盡管函式rand_r本身是執行緒安全的,但您正在使用多個執行緒seed通過函式寫入變數rand_r而沒有任何執行緒同步,這會導致未定義的行為。
即使您假設寫入unsigned int在您的平臺上是原子的,因此對同一個變數的部分寫入會導致資料損壞,您仍然會有幾個執行緒不斷覆寫seed,這有時可能會用之前的值覆寫它,所以下一次呼叫rand_r將再次生成相同的“隨機”值。這可能就是您發布的輸出連續多次具有相同“隨機”值的原因。
因此,您需要每個執行緒都有自己的seed. 一種方法是更改??線路
#pragma omp parallel for
到:
#pragma omp parallel for private(seed)
但是,這將導致每個執行緒的亂數生成器使用相同的種子值,這將導致每個執行緒的偽亂數生成器 (PRNG) 生成相同的亂數序列。根據情況,這可能是一個問題。
如果不希望每個執行緒都生成相同的亂數序列,那么可以根據omp_get_thread_num(). 這樣,每個執行緒都應該有自己的種子并生成一組不同的亂數。
但是,您必須在for回圈之外設定 PRNG 的種子,這意味著您必須將#pragma omp parallel for子句拆分為 a#pragma omp parallel和 a#pragma omp for子句:
void randomizeArray(int* array, int size, int max_value)
{
#pragma omp parallel
{
unsigned int seed = omp_get_thread_num();
#pragma omp for
for ( int i = 0; i < size; i )
{
array[i] = rand_r(&seed) % max_value;
}
}
}
轉載請註明出處,本文鏈接:https://www.uj5u.com/caozuo/409603.html
標籤:
