日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 运维知识 > linux >内容正文

linux

Linux多线程——使用信号量同步线程

發布時間:2023/11/30 linux 26 豆豆
生活随笔 收集整理的這篇文章主要介紹了 Linux多线程——使用信号量同步线程 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

http://blog.csdn.net/ljianhui/article/details/10813469/

信號量、同步這些名詞在進程間通信時就已經說過,在這里它們的意思是相同的,只不過是同步的對象不同而已。但是下面介紹的信號量的接口是用于線程的信號量,注意不要跟用于進程間通信的信號量混淆,關于用于進程間通信的信號量的詳細介紹可以參閱我的另一篇博文:Linux進程間通信——使用信號量。相似地,線程同步是控制線程執行和訪問臨界區域的方法
一、什么是信號量 線程的信號量與進程間通信中使用的信號量的概念是一樣,它是一種特殊的變量,它可以被增加或減少,但對其的關鍵訪問被保證是原子操作。如果一個程序中有多個線程試圖改變一個信號量的值,系統將保證所有的操作都將依次進行。
而只有0和1兩種取值的信號量叫做二進制信號量,在這里將重點介紹。而信號量一般常用于保護一段代碼,使其每次只被一個執行線程運行。我們可以使用二進制信號量來完成這個工作。
二、信號量的接口和使用
信號量的函數都以sem_開頭,線程中使用的基本信號量函數有4個,它們都聲明在頭文件semaphore.h中。
1、sem_init函數 該函數用于創建信號量,其原型如下: [cpp]?view plain?copy
  • int?sem_init(sem_t?*sem,?int?pshared,?unsigned?int?value);??
  • 該函數初始化由sem指向的信號對象,設置它的共享選項,并給它一個初始的整數值。pshared控制信號量的類型,如果其值為0,就表示這個信號量是當前進程的局部信號量,否則信號量就可以在多個進程之間共享,value為sem的初始值。調用成功時返回0,失敗返回-1.
    2、sem_wait函數 該函數用于以原子操作的方式將信號量的值減1。原子操作就是,如果兩個線程企圖同時給一個信號量加1或減1,它們之間不會互相干擾。它的原型如下: [cpp]?view plain?copy
  • int?sem_wait(sem_t?*sem);??
  • sem指向的對象是由sem_init調用初始化的信號量。調用成功時返回0,失敗返回-1.
    3、sem_post函數 該函數用于以原子操作的方式將信號量的值加1。它的原型如下: [cpp]?view plain?copy
  • int?sem_post(sem_t?*sem);??
  • 與sem_wait一樣,sem指向的對象是由sem_init調用初始化的信號量。調用成功時返回0,失敗返回-1.
    4、sem_destroy函數 該函數用于對用完的信號量的清理。它的原型如下: [cpp]?view plain?copy
  • int?sem_destroy(sem_t?*sem);??
  • 成功時返回0,失敗時返回-1.
    三、使用信號量同步線程
    下面以一個簡單的多線程程序來說明如何使用信號量進行線程同步。在主線程中,我們創建子線程,并把數組msg作為參數傳遞給子線程,然后主線程等待直到有文本輸入,然后調用sem_post來增加信號量的值,這樣就會立刻使子線程從sem_wait的等待中返回并開始執行。線程函數在把字符串的小寫字母變成大寫并統計輸入的字符數量之后,它再次調用sem_wait并再次被阻塞,直到主線程再次調用sem_post增加信號量的值。
    [cpp]?view plain?copy
  • #include?<unistd.h>??
  • #include?<pthread.h>??
  • #include?<semaphore.h>??
  • #include?<stdlib.h>??
  • #include?<stdio.h>??
  • #include?<string.h>??
  • ??
  • //線程函數??
  • void?*thread_func(void?*msg);??
  • sem_t?sem;//信號量??
  • ??
  • #define?MSG_SIZE?512??
  • ??
  • int?main()??
  • {??
  • ????int?res?=?-1;??
  • ????pthread_t?thread;??
  • ????void?*thread_result?=?NULL;??
  • ????char?msg[MSG_SIZE];??
  • ????//初始化信號量,其初值為0??
  • ????res?=?sem_init(&sem,?0,?0);??
  • ????if(res?==?-1)??
  • ????{??
  • ????????perror("semaphore?intitialization?failed\n");??
  • ????????exit(EXIT_FAILURE);??
  • ????}??
  • ????//創建線程,并把msg作為線程函數的參數??
  • ????res?=?pthread_create(&thread,?NULL,?thread_func,?msg);??
  • ????if(res?!=?0)??
  • ????{??
  • ????????perror("pthread_create?failed\n");??
  • ????????exit(EXIT_FAILURE);??
  • ????}??
  • ????//輸入信息,以輸入end結束,由于fgets會把回車(\n)也讀入,所以判斷時就變成了“end\n”??
  • ????printf("Input?some?text.?Enter?'end'to?finish...\n");??
  • ????while(strcmp("end\n",?msg)?!=?0)??
  • ????{??
  • ????????fgets(msg,?MSG_SIZE,?stdin);??
  • ????????//把信號量加1??
  • ????????sem_post(&sem);??
  • ????}??
  • ??
  • ????printf("Waiting?for?thread?to?finish...\n");??
  • ????//等待子線程結束??
  • ????res?=?pthread_join(thread,?&thread_result);??
  • ????if(res?!=?0)??
  • ????{??
  • ????????perror("pthread_join?failed\n");??
  • ????????exit(EXIT_FAILURE);??
  • ????}??
  • ????printf("Thread?joined\n");??
  • ????//清理信號量??
  • ????sem_destroy(&sem);??
  • ????exit(EXIT_SUCCESS);??
  • }??
  • ??
  • void*?thread_func(void?*msg)??
  • {??
  • ????//把信號量減1??
  • ????sem_wait(&sem);??
  • ????char?*ptr?=?msg;??
  • ????while(strcmp("end\n",?msg)?!=?0)??
  • ????{??
  • ????????int?i?=?0;??
  • ????????//把小寫字母變成大寫??
  • ????????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);??
  • ????????//把信號量減1??
  • ????????sem_wait(&sem);??
  • ????}??
  • ????//退出線程??
  • ????pthread_exit(NULL);??
  • }??
  • 運行結果如下:


    從運行的結果來看,這個程序的確是同時在運行兩個線程,一個控制輸入,另一個控制處理統計和輸出。
    四、分析此信號量同步程序的缺陷 但是這個程序有一點點的小問題,就是這個程序依賴接收文本輸入的時間足夠長,這樣子線程才有足夠的時間在主線程還未準備好給它更多的單詞去處理和統計之前處理和統計出工作區中字符的個數。所以當我們連續快速地給它兩組不同的單詞去統計時,子線程就沒有足夠的時間支執行,但是信號量已被增加不止一次,所以字符統計線程(子線程)就會反復處理和統計字符數目,并減少信號量的值,直到它再次變成0為止。
    為了更加清楚地說明上面所說的情況,修改主線程的while循環中的代碼,如下: [cpp]?view plain?copy
  • 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);??
  • ????//把信號量加1??
  • ????sem_post(&sem);??
  • }??
  • 重新編譯程序,此時運行結果如下:


    當我們輸入TEST時,主線程向子線程提供了兩個輸入,一個是來自鍵盤的輸入,一個來自主線程復數據到msg中,然后從運行結果可以看出,運行出現了異常,沒有處理和統計從鍵盤輸入TEST的字符串而卻對復制的數據作了兩次處理。原因如上面所述。
    五、解決此缺陷的方法
    解決方法有兩個,一個就是再增加一個信號量,讓主線程等到子線程處理統計完成之后再繼續執行;另一個方法就是使用互斥量。
    下面給出用增加一個信號量的方法來解決該問題的代碼,源文件名為semthread2.c,源代碼如下: [cpp]?view plain?copy
  • #include?<unistd.h>??
  • #include?<pthread.h>??
  • #include?<semaphore.h>??
  • #include?<stdlib.h>??
  • #include?<stdio.h>??
  • #include?<string.h>??
  • ??
  • ??
  • //線程函數??
  • void?*thread_func(void?*msg);??
  • sem_t?sem;//信號量??
  • sem_t?sem_add;//增加的信號量??
  • ??
  • ??
  • #define?MSG_SIZE?512??
  • ??
  • ??
  • int?main()??
  • {??
  • ????int?res?=?-1;??
  • ????pthread_t?thread;??
  • ????void?*thread_result?=?NULL;??
  • ????char?msg[MSG_SIZE];??
  • ????//初始化信號量,初始值為0??
  • ????res?=?sem_init(&sem,?0,?0);??
  • ????if(res?==?-1)??
  • ????{??
  • ????????perror("semaphore?intitialization?failed\n");??
  • ????????exit(EXIT_FAILURE);??
  • ????}??
  • ????//初始化信號量,初始值為1??
  • ????res?=?sem_init(&sem_add,?0,?1);??
  • ????if(res?==?-1)??
  • ????{??
  • ????????perror("semaphore?intitialization?failed\n");??
  • ????????exit(EXIT_FAILURE);??
  • ????}??
  • ????//創建線程,并把msg作為線程函數的參數??
  • ????res?=?pthread_create(&thread,?NULL,?thread_func,?msg);??
  • ????if(res?!=?0)??
  • ????{??
  • ????????perror("pthread_create?failed\n");??
  • ????????exit(EXIT_FAILURE);??
  • ????}??
  • ????//輸入信息,以輸入end結束,由于fgets會把回車(\n)也讀入,所以判斷時就變成了“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);??
  • ????????//把信號量加1??
  • ????????sem_post(&sem);??
  • ????????//把sem_add的值減1,即等待子線程處理完成??
  • ????????sem_wait(&sem_add);??
  • ????}??
  • ??
  • ??
  • ????printf("Waiting?for?thread?to?finish...\n");??
  • ????//等待子線程結束??
  • ????res?=?pthread_join(thread,?&thread_result);??
  • ????if(res?!=?0)??
  • ????{??
  • ????????perror("pthread_join?failed\n");??
  • ????????exit(EXIT_FAILURE);??
  • ????}??
  • ????printf("Thread?joined\n");??
  • ????//清理信號量??
  • ????sem_destroy(&sem);??
  • ????sem_destroy(&sem_add);??
  • ????exit(EXIT_SUCCESS);??
  • }??
  • ??
  • ??
  • void*?thread_func(void?*msg)??
  • {??
  • ????char?*ptr?=?msg;??
  • ????//把信號量減1??
  • ????sem_wait(&sem);??
  • ????while(strcmp("end\n",?msg)?!=?0)??
  • ????{??
  • ????????int?i?=?0;??
  • ????????//把小寫字母變成大寫??
  • ????????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);??
  • ????????//把信號量加1,表明子線程處理完成??
  • ????????sem_post(&sem_add);??
  • ????????//把信號量減1??
  • ????????sem_wait(&sem);??
  • ????}??
  • ????sem_post(&sem_add);??
  • ????//退出線程??
  • ????pthread_exit(NULL);??
  • }??
  • 其運行結果如下:

    分析:這里我們多使用了一個信號量sem_add,并把它的初值賦為1,在主線程在使用sem_wait來等待子線程處理完全,由于它的初值為1,所以主線程第一次調用sem_wait總是立即返回,而第二次調用則需要等待子線程處理完成之后。而在子線程中,若處理完成就會馬上使用sem_post來增加信號量的值,使主線程中的sem_wait馬上返回并執行緊接下面的代碼。從運行結果來看,運行終于正常了。注意,在線程函數中,信號量sem和sem_add使用sem_wait和sem_post函數的次序,它們的次序不能錯亂,否則在輸入end時,可能運行不正常,子線程不能正常退出,從而導致程序不能退出。
    至于使用互斥量的方法,將會在下篇文章:Linux多線程——使用互斥量同步線程中詳細介紹。

    總結

    以上是生活随笔為你收集整理的Linux多线程——使用信号量同步线程的全部內容,希望文章能夠幫你解決所遇到的問題。

    如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。