线程控制(二)
1. 一個線程實驗的問題
#include <stdio.h> #include <stdlib.h> #include <sys/types.h> #include <unistd.h> #include <ctype.h> #include <pthread.h> #define MAX_THREAD 3 /* 線程的個數 */ unsigned long long main_counter, counter[MAX_THREAD]; pthread_mutex_t mutex;void* thread_worker(void*); int main(int argc,char* argv[]) { int i, rtn, ch; pthread_t pthread_id[MAX_THREAD] = {0}; /* 存放線程id*/ for (i=0; i<MAX_THREAD; i++){ int *t = malloc(4);// 用malloc,避免下面count[i]++的時候i的值改變(i在下面函數賦給了thread_num)*t = i;pthread_create(&pthread_id[i], NULL, thread_worker, (void *)t ); //新建子線程}do{/* 用戶按一次回車執行下面的循環體一次。按q退出 */ unsigned long long sum = 0; /* 求所有線程的counter的和 */for (i=0; i<MAX_THREAD; i++){/* 求所有counter的和 */ pthread_mutex_lock(&mutex); sum += counter[i]; // ---- 2printf("%llu ", counter[i]); }printf("%llu/%llu", main_counter, sum); pthread_mutex_unlock(&mutex);}while ((ch = getchar()) != 'q'); return 0; } void* thread_worker(void* p) { int thread_num;thread_num = *(int *)p;/* 無限循環 */ for(;;) { thread_num = *(int *)p;if(pthread_mutex_lock(&mutex) < 0){printf("lock error\n");exit(0);}counter[thread_num]++; /* 本線程的counter加一 */ main_counter++; /* 主counter 加一 */ //---- 1if(pthread_mutex_unlock(&mutex) < 0){printf("unlock error\n");exit(0);}} }現象:
若直接運行此程序,會產生多種錯誤(與預想的每行最后兩列值相等不符,且會有大于和小于兩種情況的發生)。
先說第一種小于的情況,因為自加 ( 代碼中1處 ) 的操作并非原子操作,需要t = i, t += 1, i = t三步才可以完成自加,即可以被打斷其運行,若一個線程在前兩步被打斷(設此時t = 100),而其他線程又對i進行自加到了i = 120,此時第一個線程繼續運行,問題出現了,現在t加1后再賦給i,此后i的自加是從101開始的,并非從預想的120開始自加,所以i最終的值會小于實際應該的值。
再說第二種大于的情況,問題在代碼中2處,同樣因為線程調度的原因,線程可能在sum把count[i]加和后,線程停止,其他線程運行,導致在其他線程中count[i]的值又自加了(例如代碼1處),而在輸出時則輸出已經加和的sum,而不是數值改變后的和,所以數值看起來會變小。
解決辦法:
既然我們已經找到了問題發生的原因,也就是某線程的非原子操作可能會發生中斷,導致其他線程修改了本線程將要使用變量的值,那么我們就要想辦法讓本線程在操作完之前其他線程不會改變將要使用或已使用變量的值,所以互斥量在此時就發揮出了作用。設置互斥量后就保證了當前進程對某些變量的“絕對占有權”,避免了其他線程對其影響。
只需改1、2處代碼即可:
/*修改的第一部分*/counter[thread_num]++; pthread_mutex_lock(&mutex); //加鎖 main_counter++; //---- 1 pthread_mutex_unlock(&mutex);//解鎖/*修改的第二部分*/do{unsigned long long sum = 0; pthread_mutex_lock(&mutex); //加鎖for (i=0; i<MAX_THREAD; i++){ sum += counter[i]; // ---- 2printf("%llu ", counter[i]); }printf("%llu/%llu", main_counter, sum); pthread_mutex_unlock(&mutex); //解鎖}while ((ch = getchar()) != 'q');至此,已經修改完畢。
2. 死鎖問題
該問題只要注意加鎖的順序要對應即可。
#include <stdio.h> #include <sys/types.h> #include <unistd.h> #include <ctype.h> #include <pthread.h> #define LOOP_TIMES 10000 /*用宏PTHREAD_MUTEX_INITIALIZER來初始化 */ pthread_mutex_t mutex1 = PTHREAD_MUTEX_INITIALIZER; pthread_mutex_t mutex2 = PTHREAD_MUTEX_INITIALIZER;void* thread_worker(void*); void critical_section(int thread_num, int i); int main(void) { int rtn, i; pthread_t pthread_id = 0; /* 存放子線程的id */ rtn = pthread_create(&pthread_id,NULL, thread_worker, NULL ); if(rtn != 0){ printf("pthread_create ERROR!\n"); return -1; } for (i=0; i<LOOP_TIMES; i++) { /*代碼段1*/pthread_mutex_lock(&mutex1); // 先鎖1,后鎖2pthread_mutex_lock(&mutex2); critical_section(1, i); pthread_mutex_unlock(&mutex2);pthread_mutex_unlock(&mutex1);} pthread_mutex_destroy(&mutex1); pthread_mutex_destroy(&mutex2); return 0; } void* thread_worker(void* p) { int i; for (i=0; i<LOOP_TIMES; i++){ /*代碼段2*/pthread_mutex_lock(&mutex2); // 先鎖2,后鎖1pthread_mutex_lock(&mutex1); critical_section(2, i); pthread_mutex_unlock(&mutex2);pthread_mutex_unlock(&mutex1);} } void critical_section(int thread_num, int i) { printf("Thread%d: %d\n", thread_num,i); }現象及原因:
該代碼由于加鎖順序不同,可能一個線程在代碼段1處先給mutex1加鎖,將mutex1阻塞,然后暫停運行,被另一線程搶奪了使用cpu的權限,而另一線程剛好又在代碼段2處對mutex2進行了加鎖,將mutex2阻塞。此時mutex1需要調用mutex2,而mutex2需要調用mutex1,然而更加不幸的是兩者都被阻塞,等待對方被釋放,悲劇就此產生,兩個固執的線程都在等待對方收手,而這是不可能發生的事,所以就這樣僵持下去,死鎖現象也就出現了。
解決方法:
很簡單,將加鎖順序修改的相同即可。都先鎖1,后鎖2,反之亦可。
總結
- 上一篇: 雌激素高会不会导致不孕不育
- 下一篇: 游戏崩溃