av激情亚洲男人的天堂国语,日韩欧美精品一中文字幕,无码av一区二区三区无码,国产又色又爽又刺激的a片,国产又色又爽又刺激的a片

一個多線程的簡單例子讓你看清線程調(diào)度的隨機(jī)性

線程調(diào)度的幾個基本知識點(diǎn)

專注于為中小企業(yè)提供成都網(wǎng)站設(shè)計、網(wǎng)站建設(shè)服務(wù),電腦端+手機(jī)端+微信端的三站合一,更高效的管理,為中小企業(yè)豐南免費(fèi)做網(wǎng)站提供優(yōu)質(zhì)的服務(wù)。我們立足成都,凝聚了一批互聯(lián)網(wǎng)行業(yè)人才,有力地推動了上1000家企業(yè)的穩(wěn)健成長,幫助中小企業(yè)通過網(wǎng)站建設(shè)實(shí)現(xiàn)規(guī)模擴(kuò)充和轉(zhuǎn)變。

多線程并發(fā)執(zhí)行時有很多同學(xué)捋不清楚調(diào)度的隨機(jī)性會導(dǎo)致哪些問題,要知道如果訪問臨界資源不加鎖會導(dǎo)致一些突發(fā)情況發(fā)生甚至死鎖。

關(guān)于線程調(diào)度,需要深刻了解以下幾個基礎(chǔ)知識點(diǎn):

調(diào)度的最小單位是輕量級進(jìn)程【比如我們編寫的hello world最簡單的C程序,執(zhí)行時就是一個輕量級進(jìn)程】或者線程;

每個線程都會分配一個時間片,時間片到了就會執(zhí)行下一個線程;

線程的調(diào)度有一定的隨機(jī)性,無法確定什么時候會調(diào)度;

在同一個進(jìn)程內(nèi),創(chuàng)建的所有線程除了線程內(nèi)部創(chuàng)建的局部資源,進(jìn)程創(chuàng)建的其他資源所有線程共享;比如:主線程和子線程都可以訪問全局變量,打開的文件描述符等。

實(shí)例

再多的理論不如一個形象的例子來的直接。

預(yù)期代碼時序

假定我們要實(shí)現(xiàn)一個多線程的實(shí)例,預(yù)期程序執(zhí)行時序如下:

期待時序

期待的功能時序:

  1. 主進(jìn)程創(chuàng)建子線程,子線程函數(shù)function();
  2. 主線程count自加,并分別賦值給value1,value2;
  3. 時間片到了后切換到子線程,子線程判斷value1、value2值是否相同,如果不同就打印信息value1,value2,count的值,但是因?yàn)橹骶€程將count先后賦值給了value1,value2,所以value1,value2的值應(yīng)該永遠(yuǎn)不相同,所以不應(yīng)該打印任何內(nèi)容;
  4. 重復(fù)2、3步驟。

代碼1

好了,現(xiàn)在我們按照這個時序編寫代碼如下:

 
 
 
 
  1. 1 #include  
  2.   2 #include  
  3.   3 #include  
  4.   4 #include  
  5.   5 #include  
  6.   6  
  7.   7 unsigned int value1,value2, count=0; 
  8.   8 void *function(void *arg); 
  9.   9 int main(int argc,  char *argv[]) 
  10.  10 { 
  11.  11     pthread_t  a_thread; 
  12.  12  
  13.  13     if (pthread_create(&a_thread, NULL, function, NULL) < 0) 
  14.  14     { 
  15.  15         perror("fail to pthread_create"); 
  16.  16         exit(-1); 
  17.  17     } 
  18.  18     while ( 1 ) 
  19.  19     { 
  20.  20         count++; 
  21.  21         value1 = count; 
  22.  22         value2 = count; 
  23.  23     } 
  24.  24     return 0; 
  25.  25 } 
  26.  26  
  27.  27 void  *function(void *arg) 
  28.  28 { 
  29.  29     while ( 1 ) 
  30.  30     { 
  31.  31         if (value1 != value2) 
  32.  32         {                                                                                                                                                                                          
  33.  33             printf("count=%d , value1=%d, value2=%d\n",  count, value1, value2); 
  34.  34             usleep(100000); 
  35.  35         }      
  36.  36     } 
  37.  37     return  NULL; 
  38.  38 }   

乍一看,該程序應(yīng)該可以滿足我們的需要,并且程序運(yùn)行的時候不應(yīng)該打印任何內(nèi)容,但是實(shí)際運(yùn)行結(jié)果出乎我們意料。

編譯運(yùn)行:

 
 
 
 
  1. gcc test.c -o run -lpthread 
  2. ./run 

代碼1執(zhí)行結(jié)果

執(zhí)行結(jié)果:

可以看到子程序會隨機(jī)打印一些信息,為什么還有這個執(zhí)行結(jié)果呢?其實(shí)原因很簡單,就是我們文章開頭所說的,線程調(diào)度具有?隨機(jī)性,我們無法規(guī)定讓內(nèi)核何時調(diào)度某個線程。有打印信息,那么這說明此時value1和value2的值是不同的,那也說明了調(diào)度子線程的時候,是在主線程向value1和value2之間的位置調(diào)度的。

代碼1執(zhí)行的實(shí)際時序

實(shí)際上代碼的執(zhí)行時序如下所示:

如上圖,在某一時刻,當(dāng)程序走到**value2 = count;**這個位置的時候,內(nèi)核對線程進(jìn)行了調(diào)度,于是子進(jìn)程在判斷value1和value2的值的時候,發(fā)現(xiàn)這兩個變量值不相同,就有了打印信息。

該程序在下面這兩行代碼之間調(diào)度的幾率還是很大的。

 
 
 
 
  1. value1 = count;  
  2. value2 = count; 

解決方法

如何來解決并發(fā)導(dǎo)致的程序沒有按預(yù)期執(zhí)行的問題呢?對于線程來說,常用的方法有posix信號量、互斥鎖,條件變量等,下面我們以互斥鎖為例,講解如何避免代碼1的問題的出現(xiàn)。

互斥鎖的定義和初始化:

 
 
 
 
  1. pthread_mutex_t  mutex; 
  2. pthread_mutex_init(&mutex, NULL) 

申請釋放鎖:

 
 
 
 
  1. pthread_mutex_lock(&mutex); 
  2. pthread_mutex_unlock(&mutex); 

原理:進(jìn)入臨界區(qū)之前先申請鎖,如果能獲得鎖就繼續(xù)往下執(zhí)行, 如果申請不到,就休眠,直到其他線程釋放該鎖為止。

代碼2

 
 
 
 
  1. 1 #include  
  2.  2 #include  
  3.  3 #include  
  4.  4 #include  
  5.  5 #include  
  6.  6 #define _LOCK_ 
  7.  7 unsigned int value1,value2, count=0; 
  8.  8 pthread_mutex_t  mutex; 
  9.  9 void *function(void *arg); 
  10. 10  
  11. 11 int main(int argc,  char *argv[]) 
  12. 12 { 
  13. 13     pthread_t  a_thread; 
  14. 14           
  15. 15     if (pthread_mutex_init(&mutex, NULL) < 0)                                                                                                                                                           
  16. 16     { 
  17. 17         perror("fail to mutex_init"); 
  18. 18         exit(-1); 
  19. 19     } 
  20. 20  
  21. 21     if (pthread_create(&a_thread, NULL, function, NULL) < 0) 
  22. 22     { 
  23. 23         perror("fail to pthread_create"); 
  24. 24         exit(-1); 
  25. 25     } 
  26. 26     while ( 1 ) 
  27. 27     { 
  28. 28         count++; 
  29. 29 #ifdef  _LOCK_ 
  30. 30         pthread_mutex_lock(&mutex); 
  31. 31 #endif 
  32. 32         value1 = count; 
  33. 33         value2 = count; 
  34. 34 #ifdef  _LOCK_ 
  35. 35         pthread_mutex_unlock(&mutex); 
  36. 36 #endif 
  37. 37     } 
  38. 38     return 0; 
  39. 39  } 
  40. 0  
  41. 41 void  *function(void *arg) 
  42. 42 { 
  43. 43      while ( 1 ) 
  44. 44      { 
  45. 45 #ifdef _LOCK_ 
  46. 46         pthread_mutex_lock(&mutex); 
  47. 47 #endif            
  48. 48  
  49. 49         if (value1 != value2)   
  50. 50         { 
  51. 51             printf("count=%d , value1=%d, value2=%d\n",  count, value1, value2); 
  52. 52             usleep(100000); 
  53. 53         }      
  54. 54 #ifdef _LOCK_ 
  55. 55         pthread_mutex_unlock(&mutex); 
  56. 56 #endif 
  57. 57      } 
  58. 58      return  NULL; 
  59. 59  }      

如上述代碼所示:主線程和子線程要訪問臨界資源value1,value2時,都必須先申請鎖,獲得鎖之后才可以訪問臨界資源,訪問完畢再釋放互斥鎖。該代碼執(zhí)行之后就不會打印任何信息。我們來看下,如果程序在下述代碼之間產(chǎn)生調(diào)度時,程序的時序圖。

 
 
 
 
  1. value1 = count;  
  2. value2 = count; 

時序圖如下:

如上圖所示:

  1. 時刻n,主線程獲得mutex,從而進(jìn)入臨界區(qū);
  2. 時刻n+1,時間片到了,切換到子線程;
  3. n+2時刻子線程申請不到鎖mutex,所以放棄cpu,進(jìn)入休眠;
  4. n+3時刻,主線程釋放mutex,離開臨界區(qū),并喚醒阻塞在mutex的子線程,子線程申請到mutex,進(jìn)入臨界區(qū);
  5. n+4時刻,子線程離開臨界區(qū),釋放mutex。

可以看到,加鎖之后,即使主線程在value2 =count; 之前產(chǎn)生了調(diào)度,子線程由于獲取不到mutex,會進(jìn)入休眠,只有主線程出了臨界區(qū),子線程才能獲得mutex,訪問value1和value2,就永遠(yuǎn)不會打印信息,就實(shí)現(xiàn)了我們預(yù)期的代碼時序。

總結(jié)

實(shí)際項目中,可能程序的并發(fā)的情況可能會更加復(fù)雜,比如多個cpu上運(yùn)行的任務(wù)之間,cpu運(yùn)行的任務(wù)和中斷之間,中斷和中斷之間,都有可能并發(fā)。

有些調(diào)度的概率雖然很小,但是不代表不發(fā)生,而且由于資源同步互斥導(dǎo)致的問題,很難復(fù)現(xiàn),縱觀Linux內(nèi)核代碼,所有的臨界資源都會對應(yīng)鎖。

多閱讀Linux內(nèi)核源碼,學(xué)向大神學(xué)習(xí),與大神神交。

正所謂代碼讀百遍,其義自見!熟讀代碼千萬行,不會編寫也會抄!

關(guān)于內(nèi)核和應(yīng)用程序的同步互斥的知識點(diǎn),可以查看一口君的其他文章。

本文轉(zhuǎn)載自微信公眾號「一口Linux」,可以通過以下二維碼關(guān)注。轉(zhuǎn)載本文請聯(lián)系一口Linux公眾號。


名稱欄目:一個多線程的簡單例子讓你看清線程調(diào)度的隨機(jī)性
路徑分享:http://uogjgqi.cn/article/dpheood.html
掃二維碼與項目經(jīng)理溝通

我們在微信上24小時期待你的聲音

解答本文疑問/技術(shù)咨詢/運(yùn)營咨詢/技術(shù)建議/互聯(lián)網(wǎng)交流