欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

詳解Linux多線程使用信號(hào)量同步

 更新時(shí)間:2016年10月22日 15:21:35   作者:ljianhui  
本篇文章主要是介紹了Linux多線程使用信號(hào)量同步,詳細(xì)講訴了信號(hào)量的接口和使用,有需要的朋友可以了解一下。

信號(hào)量、同步這些名詞在進(jìn)程間通信時(shí)就已經(jīng)說(shuō)過(guò),在這里它們的意思是相同的,只不過(guò)是同步的對(duì)象不同而已。但是下面介紹的信號(hào)量的接口是用于線程的信號(hào)量,注意不要跟用于進(jìn)程間通信的信號(hào)量混淆。

一、什么是信號(hào)量

線程的信號(hào)量與進(jìn)程間通信中使用的信號(hào)量的概念是一樣,它是一種特殊的變量,它可以被增加或減少,但對(duì)其的關(guān)鍵訪問(wèn)被保證是原子操作。如果一個(gè)程序中有多個(gè)線程試圖改變一個(gè)信號(hào)量的值,系統(tǒng)將保證所有的操作都將依次進(jìn)行。

而只有0和1兩種取值的信號(hào)量叫做二進(jìn)制信號(hào)量,在這里將重點(diǎn)介紹。而信號(hào)量一般常用于保護(hù)一段代碼,使其每次只被一個(gè)執(zhí)行線程運(yùn)行。我們可以使用二進(jìn)制信號(hào)量來(lái)完成這個(gè)工作。

二、信號(hào)量的接口和使用

信號(hào)量的函數(shù)都以sem_開(kāi)頭,線程中使用的基本信號(hào)量函數(shù)有4個(gè),它們都聲明在頭文件semaphore.h中。

1、sem_init函數(shù)

該函數(shù)用于創(chuàng)建信號(hào)量,其原型如下:

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

該函數(shù)初始化由sem指向的信號(hào)對(duì)象,設(shè)置它的共享選項(xiàng),并給它一個(gè)初始的整數(shù)值。pshared控制信號(hào)量的類型,如果其值為0,就表示這個(gè)信號(hào)量是當(dāng)前進(jìn)程的局部信號(hào)量,否則信號(hào)量就可以在多個(gè)進(jìn)程之間共享,value為sem的初始值。調(diào)用成功時(shí)返回0,失敗返回-1.

2、sem_wait函數(shù)

該函數(shù)用于以原子操作的方式將信號(hào)量的值減1。原子操作就是,如果兩個(gè)線程企圖同時(shí)給一個(gè)信號(hào)量加1或減1,它們之間不會(huì)互相干擾。它的原型如下:

int sem_wait(sem_t *sem); 

sem指向的對(duì)象是由sem_init調(diào)用初始化的信號(hào)量。調(diào)用成功時(shí)返回0,失敗返回-1.

3、sem_post函數(shù)

該函數(shù)用于以原子操作的方式將信號(hào)量的值加1。它的原型如下:

int sem_post(sem_t *sem); 

與sem_wait一樣,sem指向的對(duì)象是由sem_init調(diào)用初始化的信號(hào)量。調(diào)用成功時(shí)返回0,失敗返回-1.

4、sem_destroy函數(shù)

該函數(shù)用于對(duì)用完的信號(hào)量的清理。它的原型如下:

int sem_destroy(sem_t *sem); 

成功時(shí)返回0,失敗時(shí)返回-1.

三、使用信號(hào)量同步線程

下面以一個(gè)簡(jiǎn)單的多線程程序來(lái)說(shuō)明如何使用信號(hào)量進(jìn)行線程同步。在主線程中,我們創(chuàng)建子線程,并把數(shù)組msg作為參數(shù)傳遞給子線程,然后主線程等待直到有文本輸入,然后調(diào)用sem_post來(lái)增加信號(hào)量的值,這樣就會(huì)立刻使子線程從sem_wait的等待中返回并開(kāi)始執(zhí)行。線程函數(shù)在把字符串的小寫(xiě)字母變成大寫(xiě)并統(tǒng)計(jì)輸入的字符數(shù)量之后,它再次調(diào)用sem_wait并再次被阻塞,直到主線程再次調(diào)用sem_post增加信號(hào)量的值。

#include <unistd.h> 
#include <pthread.h> 
#include <semaphore.h> 
#include <stdlib.h> 
#include <stdio.h> 
#include <string.h> 
 
//線程函數(shù) 
void *thread_func(void *msg); 
sem_t sem;//信號(hào)量 
 
#define MSG_SIZE 512 
 
int main() 
{ 
  int res = -1; 
  pthread_t thread; 
  void *thread_result = NULL; 
  char msg[MSG_SIZE]; 
  //初始化信號(hào)量,其初值為0 
  res = sem_init(&sem, 0, 0); 
  if(res == -1) 
  { 
    perror("semaphore intitialization failed\n"); 
    exit(EXIT_FAILURE); 
  } 
  //創(chuàng)建線程,并把msg作為線程函數(shù)的參數(shù) 
  res = pthread_create(&thread, NULL, thread_func, msg); 
  if(res != 0) 
  { 
    perror("pthread_create failed\n"); 
    exit(EXIT_FAILURE); 
  } 
  //輸入信息,以輸入end結(jié)束,由于fgets會(huì)把回車(\n)也讀入,所以判斷時(shí)就變成了“end\n” 
  printf("Input some text. Enter 'end'to finish...\n"); 
  while(strcmp("end\n", msg) != 0) 
  { 
    fgets(msg, MSG_SIZE, stdin); 
    //把信號(hào)量加1 
    sem_post(&sem); 
  } 
 
  printf("Waiting for thread to finish...\n"); 
  //等待子線程結(jié)束 
  res = pthread_join(thread, &thread_result); 
  if(res != 0) 
  { 
    perror("pthread_join failed\n"); 
    exit(EXIT_FAILURE); 
  } 
  printf("Thread joined\n"); 
  //清理信號(hào)量 
  sem_destroy(&sem); 
  exit(EXIT_SUCCESS); 
} 
 
void* thread_func(void *msg) 
{ 
  //把信號(hào)量減1 
  sem_wait(&sem); 
  char *ptr = msg; 
  while(strcmp("end\n", msg) != 0) 
  { 
    int i = 0; 
    //把小寫(xiě)字母變成大寫(xiě) 
    for(; ptr[i] != '\0'; ++i) 
    { 
      if(ptr[i] >= 'a' && ptr[i] <= 'z') 
      { 
        ptr[i] -= 'a' - 'A'; 
      } 
    } 
    printf("You input %d characters\n", i-1); 
    printf("To Uppercase: %s\n", ptr); 
    //把信號(hào)量減1 
    sem_wait(&sem); 
  } 
  //退出線程 
  pthread_exit(NULL); 
} 

運(yùn)行結(jié)果如下:

從運(yùn)行的結(jié)果來(lái)看,這個(gè)程序的確是同時(shí)在運(yùn)行兩個(gè)線程,一個(gè)控制輸入,另一個(gè)控制處理統(tǒng)計(jì)和輸出。

四、分析此信號(hào)量同步程序的缺陷

但是這個(gè)程序有一點(diǎn)點(diǎn)的小問(wèn)題,就是這個(gè)程序依賴接收文本輸入的時(shí)間足夠長(zhǎng),這樣子線程才有足夠的時(shí)間在主線程還未準(zhǔn)備好給它更多的單詞去處理和統(tǒng)計(jì)之前處理和統(tǒng)計(jì)出工作區(qū)中字符的個(gè)數(shù)。所以當(dāng)我們連續(xù)快速地給它兩組不同的單詞去統(tǒng)計(jì)時(shí),子線程就沒(méi)有足夠的時(shí)間支執(zhí)行,但是信號(hào)量已被增加不止一次,所以字符統(tǒng)計(jì)線程(子線程)就會(huì)反復(fù)處理和統(tǒng)計(jì)字符數(shù)目,并減少信號(hào)量的值,直到它再次變成0為止。

為了更加清楚地說(shuō)明上面所說(shuō)的情況,修改主線程的while循環(huán)中的代碼,如下:

printf("Input some text. Enter 'end'to finish...\n"); 
while(strcmp("end\n", msg) != 0) 
{ 
  if(strncmp("TEST", msg, 4) == 0) 
  { 
    strcpy(msg, "copy_data\n"); 
    sem_post(&sem); 
  } 
  fgets(msg, MSG_SIZE, stdin); 
  //把信號(hào)量加1 
  sem_post(&sem); 
} 

重新編譯程序,此時(shí)運(yùn)行結(jié)果如下:

當(dāng)我們輸入TEST時(shí),主線程向子線程提供了兩個(gè)輸入,一個(gè)是來(lái)自鍵盤(pán)的輸入,一個(gè)來(lái)自主線程復(fù)數(shù)據(jù)到msg中,然后從運(yùn)行結(jié)果可以看出,運(yùn)行出現(xiàn)了異常,沒(méi)有處理和統(tǒng)計(jì)從鍵盤(pán)輸入TEST的字符串而卻對(duì)復(fù)制的數(shù)據(jù)作了兩次處理。原因如上面所述。

五、解決此缺陷的方法

解決方法有兩個(gè),一個(gè)就是再增加一個(gè)信號(hào)量,讓主線程等到子線程處理統(tǒng)計(jì)完成之后再繼續(xù)執(zhí)行;另一個(gè)方法就是使用互斥量。

下面給出用增加一個(gè)信號(hào)量的方法來(lái)解決該問(wèn)題的代碼,源文件名為semthread2.c,源代碼如下:

#include <unistd.h> 
#include <pthread.h> 
#include <semaphore.h> 
#include <stdlib.h> 
#include <stdio.h> 
#include <string.h> 
 
 
//線程函數(shù) 
void *thread_func(void *msg); 
sem_t sem;//信號(hào)量 
sem_t sem_add;//增加的信號(hào)量 
 
 
#define MSG_SIZE 512 
 
 
int main() 
{ 
  int res = -1; 
  pthread_t thread; 
  void *thread_result = NULL; 
  char msg[MSG_SIZE]; 
  //初始化信號(hào)量,初始值為0 
  res = sem_init(&sem, 0, 0); 
  if(res == -1) 
  { 
    perror("semaphore intitialization failed\n"); 
    exit(EXIT_FAILURE); 
  } 
  //初始化信號(hào)量,初始值為1 
  res = sem_init(&sem_add, 0, 1); 
  if(res == -1) 
  { 
    perror("semaphore intitialization failed\n"); 
    exit(EXIT_FAILURE); 
  } 
  //創(chuàng)建線程,并把msg作為線程函數(shù)的參數(shù) 
  res = pthread_create(&thread, NULL, thread_func, msg); 
  if(res != 0) 
  { 
    perror("pthread_create failed\n"); 
    exit(EXIT_FAILURE); 
  } 
  //輸入信息,以輸入end結(jié)束,由于fgets會(huì)把回車(\n)也讀入,所以判斷時(shí)就變成了“end\n” 
  printf("Input some text. Enter 'end'to finish...\n"); 
   
  sem_wait(&sem_add); 
  while(strcmp("end\n", msg) != 0) 
  { 
    if(strncmp("TEST", msg, 4) == 0) 
    { 
      strcpy(msg, "copy_data\n"); 
      sem_post(&sem); 
      //把sem_add的值減1,即等待子線程處理完成 
      sem_wait(&sem_add); 
    } 
    fgets(msg, MSG_SIZE, stdin); 
    //把信號(hào)量加1 
    sem_post(&sem); 
    //把sem_add的值減1,即等待子線程處理完成 
    sem_wait(&sem_add); 
  } 
 
 
  printf("Waiting for thread to finish...\n"); 
  //等待子線程結(jié)束 
  res = pthread_join(thread, &thread_result); 
  if(res != 0) 
  { 
    perror("pthread_join failed\n"); 
    exit(EXIT_FAILURE); 
  } 
  printf("Thread joined\n"); 
  //清理信號(hào)量 
  sem_destroy(&sem); 
  sem_destroy(&sem_add); 
  exit(EXIT_SUCCESS); 
} 
 
 
void* thread_func(void *msg) 
{ 
  char *ptr = msg; 
  //把信號(hào)量減1 
  sem_wait(&sem); 
  while(strcmp("end\n", msg) != 0) 
  { 
    int i = 0; 
    //把小寫(xiě)字母變成大寫(xiě) 
    for(; ptr[i] != '\0'; ++i) 
    { 
      if(ptr[i] >= 'a' && ptr[i] <= 'z') 
      { 
        ptr[i] -= 'a' - 'A'; 
      } 
    } 
    printf("You input %d characters\n", i-1); 
    printf("To Uppercase: %s\n", ptr); 
    //把信號(hào)量加1,表明子線程處理完成 
    sem_post(&sem_add); 
    //把信號(hào)量減1 
    sem_wait(&sem); 
  } 
  sem_post(&sem_add); 
  //退出線程 
  pthread_exit(NULL); 

其運(yùn)行結(jié)果如下:

分析:這里我們多使用了一個(gè)信號(hào)量sem_add,并把它的初值賦為1,在主線程在使用sem_wait來(lái)等待子線程處理完全,由于它的初值為1,所以主線程第一次調(diào)用sem_wait總是立即返回,而第二次調(diào)用則需要等待子線程處理完成之后。而在子線程中,若處理完成就會(huì)馬上使用sem_post來(lái)增加信號(hào)量的值,使主線程中的sem_wait馬上返回并執(zhí)行緊接下面的代碼。從運(yùn)行結(jié)果來(lái)看,運(yùn)行終于正常了。注意,在線程函數(shù)中,信號(hào)量sem和sem_add使用sem_wait和sem_post函數(shù)的次序,它們的次序不能錯(cuò)亂,否則在輸入end時(shí),可能運(yùn)行不正常,子線程不能正常退出,從而導(dǎo)致程序不能退出。

以上就是本文的全部?jī)?nèi)容,希望對(duì)大家的學(xué)習(xí)有所幫助,也希望大家多多支持腳本之家。

相關(guān)文章

  • 火山互聯(lián)linux VPS配置方法

    火山互聯(lián)linux VPS配置方法

    火山互聯(lián)推出的免費(fèi)VPS...拿來(lái)學(xué)習(xí)下還是不錯(cuò)的.申請(qǐng)過(guò)幾次Liunx系統(tǒng)的.由于不是很熟悉.幾次都沒(méi)能配置好.下面介紹一下.前幾天又申請(qǐng)了一個(gè).
    2009-12-12
  • Linux 中有效用戶組和初始用戶組的實(shí)現(xiàn)

    Linux 中有效用戶組和初始用戶組的實(shí)現(xiàn)

    這篇文章主要介紹了Linux 中有效用戶組和初始用戶組的實(shí)現(xiàn),文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧
    2019-11-11
  • Linux下Oracle設(shè)置定時(shí)任務(wù)備份數(shù)據(jù)庫(kù)的教程

    Linux下Oracle設(shè)置定時(shí)任務(wù)備份數(shù)據(jù)庫(kù)的教程

    這篇文章主要介紹了Linux下Oracle設(shè)置定時(shí)任務(wù)備份數(shù)據(jù)庫(kù)的方法,本文給大家介紹的非常詳細(xì),具有一定的參考借鑒價(jià)值,需要的朋友可以參考下
    2019-11-11
  • Linux NFS服務(wù)器安裝與配置思路分析

    Linux NFS服務(wù)器安裝與配置思路分析

    NFS 是Network File System的縮寫(xiě),即網(wǎng)絡(luò)文件系統(tǒng),可以讓不同的客戶端掛載使用同一個(gè)目錄,作為共享存儲(chǔ)使用,這樣可以保證不同的節(jié)點(diǎn)客戶端數(shù)據(jù)一致性,在集群架構(gòu)中經(jīng)常用到.這篇文章主要介紹了Linux NFS服務(wù)器安裝與配置思路,需要的朋友可以參考下
    2016-10-10
  • 詳解Nginx服務(wù)器中的Socket切分

    詳解Nginx服務(wù)器中的Socket切分

    這篇文章主要介紹了Nginx服務(wù)器中的Socket切分,以新發(fā)布的1.9.1版本的Nginx為基礎(chǔ),需要的朋友可以參考下
    2015-06-06
  • linux托盤(pán)不斷閃爍之解決方法

    linux托盤(pán)不斷閃爍之解決方法

    紅旗6sp1雖然升級(jí)了一些配置和軟件,但是也出現(xiàn)很多問(wèn)題 比較突出的就是 當(dāng)通過(guò) 網(wǎng)絡(luò) 設(shè)置 一個(gè)pppoe adsl 上網(wǎng)以后,系統(tǒng)托盤(pán)不停的閃爍
    2008-07-07
  • linux tomcat配置https的方法

    linux tomcat配置https的方法

    這篇文章主要介紹了linux tomcat配置https的方法,需要的朋友可以參考下
    2017-08-08
  • 新手學(xué)習(xí)Linux系統(tǒng)的11點(diǎn)建議

    新手學(xué)習(xí)Linux系統(tǒng)的11點(diǎn)建議

    這篇文章主要為大家詳細(xì)介紹了新手學(xué)習(xí)Linux系統(tǒng)的11點(diǎn)建議,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下
    2016-09-09
  • CentOS 6.1 環(huán)境中部署nginx、php(包括fastcgi)、虛擬主機(jī)配置

    CentOS 6.1 環(huán)境中部署nginx、php(包括fastcgi)、虛擬主機(jī)配置

    CentOS 6.1 環(huán)境中部署nginx、php(包括fastcgi)、虛擬主機(jī)配置,需要的朋友可以參考下
    2012-08-08
  • Linux下安裝軟件包報(bào)依賴等相關(guān)問(wèn)題的解決方法

    Linux下安裝軟件包報(bào)依賴等相關(guān)問(wèn)題的解決方法

    大家好,本篇文章主要講的是Linux下安裝軟件包報(bào)依賴等相關(guān)問(wèn)題的解決方法,感興趣的同學(xué)趕快來(lái)看一看吧,對(duì)你有幫助的話,記得收藏一下
    2021-12-12

最新評(píng)論