信號量、同步這些名詞在進程間通信時就已經說過,在這里它們的意思是相同的,只不過是同步的對象不同而已。但是下面介紹的信號量的接口是用于線程的信號量,注意不要跟用于進程間通信的信號量混淆。
一、什么是信號量
線程的信號量與進程間通信中使用的信號量的概念是一樣,它是一種特殊的變量,它可以被增加或減少,但對其的關鍵訪問被保證是原子操作。如果一個程序中有多個線程試圖改變一個信號量的值,系統將保證所有的操作都將依次進行。
而只有0和1兩種取值的信號量叫做二進制信號量,在這里將重點介紹。而信號量一般常用于保護一段代碼,使其每次只被一個執行線程運行。我們可以使用二進制信號量來完成這個工作。
二、信號量的接口和使用
信號量的函數都以sem_開頭,線程中使用的基本信號量函數有4個,它們都聲明在頭文件semaphore.h中。
1、sem_init函數
該函數用于創建信號量,其原型如下:
int sem_init(sem_t *sem, int pshared, unsigned int value);
該函數初始化由sem指向的信號對象,設置它的共享選項,并給它一個初始的整數值。pshared控制信號量的類型,如果其值為0,就表示這個信號量是當前進程的局部信號量,否則信號量就可以在多個進程之間共享,value為sem的初始值。調用成功時返回0,失敗返回-1.
2、sem_wait函數
該函數用于以原子操作的方式將信號量的值減1。原子操作就是,如果兩個線程企圖同時給一個信號量加1或減1,它們之間不會互相干擾。它的原型如下:
int sem_wait(sem_t *sem);
sem指向的對象是由sem_init調用初始化的信號量。調用成功時返回0,失敗返回-1.
3、sem_post函數
該函數用于以原子操作的方式將信號量的值加1。它的原型如下:
int sem_post(sem_t *sem);
與sem_wait一樣,sem指向的對象是由sem_init調用初始化的信號量。調用成功時返回0,失敗返回-1.
4、sem_destroy函數
該函數用于對用完的信號量的清理。它的原型如下:
int sem_destroy(sem_t *sem);
成功時返回0,失敗時返回-1.
三、使用信號量同步線程
下面以一個簡單的多線程程序來說明如何使用信號量進行線程同步。在主線程中,我們創建子線程,并把數組msg作為參數傳遞給子線程,然后主線程等待直到有文本輸入,然后調用sem_post來增加信號量的值,這樣就會立刻使子線程從sem_wait的等待中返回并開始執行。線程函數在把字符串的小寫字母變成大寫并統計輸入的字符數量之后,它再次調用sem_wait并再次被阻塞,直到主線程再次調用sem_post增加信號量的值。
#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); }
新聞熱點
疑難解答