#include <stdio.h>
#include <pthread.h>
long mails = 0;
int lock = 0;
void *routine()
{
printf("Thread Start\n");
for (long i = 0; i < 100000; i )
{
while (lock)
{
}
lock = 1;
mails ;
lock = 0;
}
printf("Thread End\n");
}
int main(int argc, int *argv[])
{
pthread_t p1, p2;
if (pthread_create(&p1, NULL, &routine, NULL) != 0)
{
return 1;
}
if (pthread_create(&p2, NULL, &routine, NULL) != 0)
{
return 2;
}
if (pthread_join(p1, NULL) != 0)
{
return 3;
}
if (pthread_join(p2, NULL) != 0)
{
return 4;
}
printf("Number of mails: %ld \n", mails);
return 0;
}
- 在上面的代碼中,每個執行緒都運行一個 for 回圈來將 mails 的值增加 100000。
- 為避免競爭條件,將鎖變數與 while 回圈一起使用。
- 在例程函式中使用 while 回圈無助于避免競爭條件并為 mails 變數提供正確的輸出。
uj5u.com熱心網友回復:
在 C 語言中,編譯器可以安全地假設(全域)變數不被其他執行緒修改,除非在少數情況下(例如 volatile 變數、原子訪問)。這意味著編譯器可以假設lock沒有修改并且while (lock) {}可以用無限回圈替換。事實上,這種回圈會導致未定義的行為,因為它沒有任何可見的效果。這意味著編譯器可以洗掉它(或生成錯誤代碼)。編譯器也可以洗掉該lock = 1陳述句,因為它后面跟著lock = 0. 結果代碼是偽造的。請注意,即使編譯器會生成正確的代碼,某些處理器(例如 AFAIK ARM 和 PowerPC)也會重新排序指令,從而導致虛假行為。
為了確保多個執行緒之間的訪問是正確的,您至少需要對lock. 原子訪問應與適當的記憶體屏障相結合,以實作輕松的原子訪問。事情是while (lock) {}會導致自旋鎖。眾所周知,自旋鎖在許多情況下是一個非常糟糕的解決方案,除非您真的知道自己在做什么以及所有后果(如有疑問,請不要使用它們)。
通常,在這種情況下最好使用互斥鎖、信號量和等待條件。互斥鎖通常在內部使用原子布爾標志實作(具有正確的記憶體屏障,因此您無需關心)。當標志被標記為鎖定時,呼叫 OS 休眠函式。當鎖被另一個執行緒釋放時,休眠函式喚醒。這是可能的,因為釋放鎖的執行緒可以發送喚醒信號。有關這方面的更多資訊,請閱讀此內容。在舊的 C 語言中,您可以使用 pthread 。從 C11 開始,您可以直接使用此標準 API執行此操作。對于 pthread,它就在這里(不要忘記初始化)。
uj5u.com熱心網友回復:
如果你真的想要一個自旋鎖,你需要這樣的東西:
#include <stdatomic.h>
atomic_flag lock = ATOMIC_FLAG_INIT;
void *routine()
{
printf("Thread Start\n");
for (long i = 0; i < 100000; i )
{
while (atomic_flag_test_and_set(&lock)) {}
mails ;
atomic_flag_clear(&lock);
}
printf("Thread End\n");
}
但是,由于您已經在使用 pthreads,因此最好使用pthread_mutex
uj5u.com熱心網友回復:
Jér?me Richard 向您介紹了編譯器可以優化代碼感覺的方式,但即使您關閉了所有優化,您仍然會遇到競爭條件。你寫了
while (lock) { }
lock=1;
...critical section...
lock=0;
問題是,假設lock==0。兩個執行緒同時跑向那個臨界區,都可以測驗lock,而且都可以找到lock==0。然后他們都會設定lock=1,他們都會進入臨界區......
...同時。
為了實作自旋鎖,*您需要一個執行緒lock在第一個執行緒測驗它和第一個執行緒設定它之間訪問變數的某種方法。您需要一個原子的(即不可分割的)“測驗和設定”操作。
大多數計算機體系結構都有某種專門的操作碼,可以滿足您的需求。它有“測驗和設定”、“比較和交換”、“加載鏈接和存盤條件”等名稱。克里斯多德的回答向您展示了如何使用標準 C 庫函式在任何 CPU 上執行正確的操作碰巧在使用...
...但不要忘記 Jér?me 說過的話。*
* Jér?me 告訴過你自旋鎖是個壞主意。
轉載請註明出處,本文鏈接:https://www.uj5u.com/houduan/512706.html
標籤:C多线程并发线程竞争条件
上一篇:使用scanf()進行輸入驗證
下一篇:如何在C中撰寫多個內核
