linux多線程同步之互斥鎖、信號量、條件量

  目錄

  一.線程同步之信號量

  1、任務:用戶從終端輸入任意字符然後統計個數顯示,輸入end則結束

  2、信號量的介紹和使用(多線程實行的引入)

  (1)、信號量初始化

  (2)、等待信號量

  (3)、釋放信號量

  (4)、銷燬信號量

  3、使用多線程實現:主線程獲取用戶輸入並判斷是否退出,子線程計數

  二.線程同步之互斥鎖

  1、什麼是互斥鎖

  (1)互斥鎖又叫互斥量(mutex)

  (2)相關函數:

  2、用互斥鎖來實現上節的代碼,

  三.線程同步之條件變量

  1、什麼是條件變量

  2、相關函數

  3、使用條件變量來實現上節代碼

  一.線程同步之信號量

  1、任務:用戶從終端輸入任意字符然後統計個數顯示,輸入end則結束

  int main(void)

  {

  char buf[100] = {0};

  printf("請輸入一個字符串,以回車結束\n");

  while(scanf("%s",buf))

  {

  //去比較輸入的是否爲end,是則退出,不是則繼續

  if(strncmp(buf,"end",3)==0)

  {

  printf("程序結束\n");

  return 0;

  }

  printf("本次輸入了%d個字符\n",strlen(buf));

  memset(buf,0,sizeof(buf));

  }

  return 0;

  }

  2、信號量的介紹和使用(多線程實行的引入)

  與進程一樣,線程也可以使用信號量來通信。線程使用信號量同步線程的步驟如下:

  (1)、信號量初始化

  int sem_init (sem_t *sem , int pshared, unsigned int value);

  對sem指定的信號量進行初始化,pshared:設置好共享選項(linux只支持爲0,即表示它是當前進程的局部信號量),然後給它一個初始值VALUE。

  (2)、等待信號量

  int sem_wait(sem_t *sem);

  給信號量減1,然後等待直到信號量的值大於0。

  (3)、釋放信號量

  int sem_post(sem_t *sem);

  信號量值加1。並通知其他等待線程。

  (4)、銷燬信號量

  int sem_destroy(sem_t *sem);

  3、使用多線程實現:主線程獲取用戶輸入並判斷是否退出,子線程計數

  E:\Linux\3.AppNet\6.thread\6.3

  char buf[100] = {0};

  sem_t sem;

  unsigned int flag=0;

  void *func(void *arg)//子線程計數

  {

  //子線程首先應該有個循環

  /*循環中阻塞在等待主線程激活的時候,子線程被激活後就去獲取buf中的字符長度,然後打印,完成後再次阻塞*/

  while(flag==0)

  {

  //②等待信號量,信號量沒來之前一直阻塞在這裏

  sem_wait(&sem);

  printf("本次輸入了%d個字符\n",strlen(buf));

  memset(buf,0,sizeof(buf));//清除buf

  sem_wait(&sem);

  }

  pthread_exit(NULL);//線程終止

  }

  int main(void)

  {

  pthread_t th = -1;

  int ret = -1;

  //①信號量初始化

  sem_init (&sem , 0, 0);

  /* 創建線程 */

  ret = pthread_create(&th, NULL, func, NULL);

  if(ret !=0)

  {

  perror("pthread_create error.\n");

  return -1;

  }

  printf("請輸入一個字符串,以回車結束\n");

  while(scanf("%s",buf))

  {

  //去比較輸入的是否爲end,是則退出,不是則繼續

  if(strncmp(buf,"end",3)==0)

  {

  printf("程序結束\n");

  //return 0;

  flag=1;//標誌量爲1.則結束

  //③釋放信號量

  sem_post(&sem);

  break;

  }

  /*主線程在收到用戶收入的字符串,並且確認不是end後,就去發信號激活子 進程來計數*/

  //子線程被阻塞,主線程可以激活,這就是線程的同步問題。

  //信號量就可以用來實現線程同步

  sem_post(&sem);//繼續時也釋放信號量

  }

  //回收子線程

  printf("等待回收子線程\n");

  ret=pthread_join(th, NULL );

  if(ret!=0)

  {

  perror("pthread_join error.\n");

  return -1;

  }

  printf("子線程回收成功\n");

  //④銷燬信號量

  sem_destroy(&sem);//銷燬信號量

  return 0;

  }

  二.線程同步之互斥鎖

  1、什麼是互斥鎖

  (1)互斥鎖又叫互斥量(mutex)

  作用:

  互斥鎖用來保證同一時間內只有一個線程在執行某段代碼(臨界區)。

  互斥鎖可看作某種意義上的全局變量。在同一時刻只能有一個線程掌握某個互斥鎖,擁有上鎖狀態的線程能夠對共享資源進行操作。若其他線程希望上鎖一個已經被上鎖的互斥鎖,則該線程就會掛起,直到上鎖的線程釋放掉互斥鎖爲止。互斥鎖保證讓每個線程對共享資源按順序進行原子操作。

  (2)相關函數:

  互斥鎖初始化: pthread_mutex_init()

  互斥鎖上鎖: pthread_mutex_lock()

  互斥鎖判斷上鎖:pthread_mutex_trylock()

  互斥鎖解鎖: pthread_mutex_unlock()

  消除互斥鎖: pthread_mutex_destroy()

  初始化互斥鎖

  int pthread_mutex_init(pthread_mutex_t *mp, const pthread_mutexattr_t *mattr);

  mp是互斥鎖地址

  mattr是屬性,通常默認 null 。初始化互斥鎖之前,必須將其所在的內存清零。如果互斥鎖已初始化,則它會處於未鎖定狀態。互斥鎖可以位於進程之間共享的內存中或者某個進程的專用內存中。

  鎖定互斥鎖

  int pthread_mutex_lock(pthread_mutex_t *mutex);

  當pthread_mutex_lock() 返回時,該互斥鎖已被鎖定。調用線程是該互斥鎖的屬主。如果該互斥鎖已被另一個線程鎖定和擁有,則調用線程將阻塞,直到該互斥鎖變爲可用爲止。

  返回值:pthread_mutex_lock() 在成功完成之後會返回零。其他任何返回值都表示出現了錯誤。如果出現以下任一情況,該函數將失敗並返回對應的值。

  EAGAIN:由於已超出了互斥鎖遞歸鎖定的最大次數,因此無法獲取該互斥鎖。

  EDEADLK:當前線程已經擁有互斥鎖。

  解除鎖定互斥鎖

  int pthread_mutex_unlock(pthread_mutex_t *mutex);

  函數說明:pthread_mutex_unlock() 可釋放 mutex 引用的互斥鎖對象。

  返回值:pthread_mutex_unlock() 在成功完成之後會返回零。其他任何返回值都表示出現了錯誤。如果出現以下情況,該函數將失敗並返回對應的值。

  EPERM :當前線程不擁有互斥鎖。

  銷燬互斥鎖

  int pthread_mutex_destroy(pthread_mutex_t *mp);

  注意,沒有釋放用來存儲互斥鎖的空間。

  返回值:pthread_mutex_destroy() 在成功完成之後會返回零。其他任何返回值都表示出現了錯誤。如果出現以下任一情況,該函數將失敗並返回對應的值。

  EINVAL: mp 指定的值不會引用已初始化的互斥鎖對象。

  2、用互斥鎖來實現上節的代碼,

  用多線程實現:主線程獲取用戶輸入並判斷是否退出,子線程計數

  E:\Linux\3.AppNet\6.thread\6.4

  char buf[100] = {0};

  pthread_mutex_t mutex;

  unsigned int flag=0;

  void *func(void *arg)//子線程計數

  {

  //子線程首先應該有個循環

  /*循環中阻塞在等待主線程激活的時候,子線程被激活後就去獲取buf中的字符長度,然後打印,完成後再次阻塞*/

  sleep(1);//先睡眠1s,讓出CPU給主線程,保證主線程先運行

  while(flag==0)

  {

  //②互斥鎖上鎖:

  pthread_mutex_lock(&mutex);

  printf("本次輸入了%d個字符\n",strlen(buf));

  memset(buf,0,sizeof(buf));//清除buf

  //③互斥鎖解鎖:

  pthread_mutex_unlock(&mutex);

  sleep(1);//再次睡眠一下,把上鎖動作交給主線程

  }

  pthread_exit(NULL);//線程終止

  }

  int main(void)

  {

  pthread_t th = -1;

  int ret = -1;

  //①初始化互斥鎖

  pthread_mutex_init(&mutex,NULL);

  /* 創建線程 */

  ret = pthread_create(&th, NULL, func, NULL);

  if(ret !=0)

  {

  perror("pthread_create error.\n");

  return -1;

  }

  //主線程

  printf("請輸入一個字符串,以回車結束\n");

  while(1)

  {

  //②互斥鎖上鎖:

  pthread_mutex_lock(&mutex);

  scanf("%s",buf);

  //③互斥鎖解鎖:

  pthread_mutex_unlock(&mutex);

  //去比較輸入的是否爲end,是則退出,不是則繼續

  if(strncmp(buf,"end",3)==0)

  {

  printf("程序結束\n");

  //return 0;

  flag=1;//標誌量爲1.則結束

  break;

  }

  sleep(1);//再次睡眠一下,把上鎖動作交給子線程

  }

  //回收子線程

  printf("等待回收子線程\n");

  ret=pthread_join(th, NULL );

  if(ret!=0)

  {

  perror("pthread_join error.\n");

  return -1;

  }

  printf("子線程回收成功\n");

  //④銷燬互斥鎖

  pthread_mutex_destroy(&mutex);

  return 0;

  }

  三.線程同步之條件變量

  1、什麼是條件變量

  與互斥鎖不同,條件變量是用來等待而不是用來上鎖的。條件變量用來自動阻塞一個線程,直到某特殊情況發生爲止。

  通常條件變量和互斥鎖同時使用。條件變量分 爲兩部分: 條件和變量。條件本身是由互斥量保護的。線程在改變條件狀態前先要鎖住互斥量。條件變量使我們可以睡眠等待某種條件出現。條件變量是利用線程間共享的全局 變量進行同步的一種機制,主要包括兩個動作:一個線程等待"條件變量的條件成立"而掛起;另一個線程使"條件成立"(給出條件成立信號)。條件的檢測是在 互斥鎖的保護下進行的。如果一個條件爲假,一個線程自動阻塞,並釋放等待狀態改變的互斥鎖。如果另一個線程改變了條件,它發信號給關聯的條件變量,喚醒一個或多個等待它的線程,重新獲得互斥鎖,重新評價條件。如果兩進程共享可讀寫的內存,條件變量可以被用來實現這兩進程間的線程同步

  條件變量用來阻塞線程等待某個事件的發生,並且當等待的事件發生時,阻塞線程會被通知。互斥鎖的缺點是隻有兩種狀態:鎖定和非鎖定。而條件變量通過允許線程阻塞和等待另一個線程發送信號的方法彌補了互斥鎖的不足,常和互斥鎖一起使用。使用時,條件變量被用來阻塞一個線程,當條件不滿足時,線程往往解開相應的互斥鎖並等待條件發生變化。一旦其它的某個線程改變了條件變量,它將通知相應的條件變量喚醒一個或多個正被此條件變量阻塞的線程。這些線程將重新鎖定互斥鎖並重新測試條件是否滿足。一般說來,條件變量被用來進行線承間的同步

  2、相關函數

  初始化條件變量

  phread_cond_init(pthread_cond_t *cond, pthread_condattr_t *cond_attr);

  等待條件成立

  釋放鎖,同時阻塞等待條件變量爲真才行。timewait()設置等待時間,仍未signal,返回ETIMEOUT(加鎖保證只有一個線程wait)

  int pthread_cond_wait(pthread_cond_t *cond, pthread_mutex_t *mutex);

  激活條件變量

  int pthread_cond_signal(pthread_cond_t *cond);

  int pthread_cond_broadcast(pthread_cond_t *cond); 解除所有線程的阻塞

  清除條件變量

  無線程等待,否則返回EBUSY

  int pthread_cond_destroy(pthread_cond_t *cond);

  3、使用條件變量來實現上節代碼

  char buf[100] = {0};

  pthread_mutex_t mutex;

  pthread_cond_t cond;

  unsigned int flag=0;

  void *func(void *arg)//子線程計數

  {

  //子線程首先應該有個循環,鄭州 不  孕 不  育 醫 院:http://wapyyk.39.net/zz3/zonghe/1d427.html/

  /*循環中阻塞在等待主線程激活的時候,子線程被激活後就去獲取buf中的字符長度,然後打印,完成後再次阻塞*/

  //sleep(1);//先睡眠1s,讓出CPU給主線程,保證主線程先運行

  //這裏的sleep也用不着了,因爲下面的pthread_cond_wait這會阻塞

  while(flag==0)

  {

  //①互斥鎖上鎖:

  pthread_mutex_lock(&mutex);

  //Ⅱ等待條件成立,即等待主線程激活條件變量

  pthread_cond_wait(&cond,&mutex);

  //再執行下面printf的計數。

  printf("本次輸入了%d個字符\n",strlen(buf));

  memset(buf,0,sizeof(buf));//清除buf

  //②互斥鎖解鎖:

  pthread_mutex_unlock(&mutex);

  //sleep(1);//再次睡眠一下,把上鎖動作交給主線程

  }

  pthread_exit(NULL);//線程終止

  }

  int main(void)

  {

  pthread_t th = -1;

  int ret = -1;

  //初始化互斥鎖

  pthread_mutex_init(&mutex,NULL);

  //Ⅰ初始化條件變量

  pthread_cond_init(&cond,NULL);

  /* 創建線程 */

  ret = pthread_create(&th, NULL, func, NULL);

  if(ret !=0)

  {

  perror("pthread_create error.\n");

  return -1;

  }

  printf("請輸入一個字符串,以回車結束\n");

  while(1)

  {

  //①互斥鎖上鎖:這裏就沒必要上鎖了。

  //pthread_mutex_lock(&mutex);

  scanf("%s",buf);

  //Ⅲ激活條件變量,喚醒正被此條件變量阻塞的子線程

  pthread_cond_signal(&cond);

  //②互斥鎖解鎖:這裏y也沒必要解鎖了 包括下面的sleep

  //pthread_mutex_unlock(&mutex);

  //去比較輸入的是否爲end,是則退出,不是則繼續

  if(strncmp(buf,"end",3)==0)

  {

  printf("程序結束\n");

  //return 0;

  flag=1;//標誌量爲1.則結束

  break;

  }

  //sleep(1);//再次睡眠一下,把上鎖動作交給子線程

  }

  //回收子線程

  printf("等待回收子線程\n");

  ret=pthread_join(th, NULL );

  if(ret!=0)

  {

  perror("pthread_join error.\n");

  return -1;

  }

  printf("子線程回收成功\n");

  //銷燬互斥鎖

  pthread_mutex_destroy(&mutex);

  //Ⅳ清除條件變量

  pthread_cond_destroy(&cond);

  return 0;

  }


發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章