前言
信號量的概念是狄克斯特拉提出的包吝,他定義了PV原語,P操作即等待通過(wait)腹纳,V操作表示釋放(post)痢掠,之所以叫做P,V驱犹,是因為狄克斯特拉是用荷蘭文定義的,P是passeren足画,V是vrijgeven雄驹;
本文重點描述了POSIX標準下的信號量使用。
信號量本質上是一個計數(shù)器淹辞,表征資源可用數(shù)量医舆。大于0時資源可訪問,小于等于0時象缀,資源不可訪問蔬将,線程只能等待;
sem_t
定義在semaphore.h頭文件中央星,其定義如下:
typedef struct {
unsigned int count;
#ifdef __LP64__
int __reserved[3];
#endif
} sem_t;
由以上代碼可見霞怀,sem_t是一個struct,在32bit程序中莉给,內(nèi)部只有一個unsigned int數(shù)據(jù)count毙石。
sem_t相關的操作如下:
int sem_destroy(sem_t* __sem);
int sem_getvalue(sem_t* __sem, int* __value);
int sem_init(sem_t* __sem, int __shared, unsigned int __value);
int sem_post(sem_t* __sem);
int sem_timedwait(sem_t* __sem, const struct timespec* __ts);
int sem_trywait(sem_t* __sem);
int sem_wait(sem_t* __sem);
sem_init
這個函數(shù)在Mac OSX下標記為deprecated,在mac下應當使用sem_open();
初始化信號量颓遏,包含3個參數(shù)徐矩,分別是信號量指針,共享模式州泊,value初始值丧蘸,分別解釋下:
- __sem: 要初始化的信號量指針
- __shared: 若為0,表示進程內(nèi)私有遥皂,只能進程內(nèi)多線程共享,若不為0刽漂,表示可以進程間共享(有說法不為0可能造成函數(shù)調(diào)用失敗演训,屬于無名信號量,待驗證)
- __value: 信號量的初值
sem_wait
阻塞當前線程贝咙,直到信號量的值大于0样悟,接觸阻塞后將信號量的值減1,表示公共資源使用后減少庭猩;
是一個原子操作窟她;
sem_post
該線程會釋放資源,使信號量的值加1蔼水;當有某個線程等待這個信號量時震糖,該線程即可以運行,并且將將信號量的值減1(參見sem_wait)趴腋;
sem_destroy
銷毀信號量, 這個函數(shù)在Mac OSX下標記為deprecated吊说,在mac下應當使用sem_close();
注意
在Mac OSX中论咏,創(chuàng)建信號量和銷毀信號量應當用sem_open和sem_close;并且創(chuàng)建的是有名信號量颁井,即信號量以文件的方式存在厅贪,可以跨進程調(diào)用。
sem_open
sem_t * sem_open(const char *, int, ...);
sem_close
int sem_close(sem_t *);
sem_unlink
int sem_unlink(const char *);
sem_unlink的作用是刪除已存在的信號量雅宾,注意有名信號量在程序結束后可能依然存在养涮,所以需要調(diào)用sem_unlink手動刪除;
舉一個??
在以下例子中眉抬,子線程作為數(shù)據(jù)生產(chǎn)者单寂,會更新num;而主線程則作為數(shù)據(jù)消費者吐辙,讀取num宣决;
#include <semaphore.h>
#include <thread>
#include <iostream>
int main(int argc, char** argv) {
// initialize semaphore
sem_unlink("sem_num");
sem_t* m_sem = sem_open("sem_num", O_CREAT|O_EXCL, S_IRWXU, 0);
int num = 0;
std::thread write_th([&]() -> void {
for(size_t i = 0; i < 100000; ++i) {
num += 1;
}
std::cout << "from write th, num is: " << num << std::endl;
sem_post(m_sem);
});
sem_wait(m_sem);
std::cout << "from read th, num is: " << num << std::endl;
sem_close(m_sem);
write_th.join();
return 0;
}
一些思考
1)當初始化sem_t的初值不為0,如給5昏苏,表示的意義尊沸,及應用場景
能否通過信號量實現(xiàn)一定意義上的線程調(diào)度?比如控制一定數(shù)量的線程運行和阻塞贤惯?可實現(xiàn)線程池(將task隊列作為資源洼专,task的數(shù)量即為信號量的數(shù)值,當線程執(zhí)行一個任務時孵构,需要wait信號量屁商,即若有任務,則執(zhí)行颈墅,并將信號量減1蜡镶,否則阻塞等待)
2)semaphore與condition variable有類似之處,兩種異同
3)信號量是否適合一個生產(chǎn)者多個消費者的場景恤筛?
我的觀點是信號量不太適合官还,雖然也可以做(方法是將消費者看做資源,消費者數(shù)量為信號量數(shù)值毒坛,通知了一個消費者望伦,則將信號量減1,否則等待消費者煎殷,優(yōu)點別扭)屯伞;這種場合更適合用條件變量去做;