linux 多執行緒之訊號量 sem_init
1. 什麼是訊號量
linux sem 訊號量是一種特殊的變數,訪問具有原子性, 用於解決程序或執行緒間共享資源引發的同步問題。
使用者態程序對 sem 訊號量可以有以下兩種操作:
- 等待訊號量
當訊號量值為 0 時,程式等待;當訊號量值大於 0 時,訊號量減 1,程式繼續執行。 - 傳送訊號量
將訊號量值加 1
通過對訊號量的控制,從而實現共享資源的順序訪問。
2. 相關函式說明
linux 訊號量相關函式都宣告標頭檔案 semaphore.h 標頭檔案中,所以使用訊號量之前需要先包含標頭檔案
#include <semaphore.h>
訊號量的建立就像宣告一般的變數一樣簡單,例如:sem_t sem,之後對該訊號量進行初始化和使用。
2.1 sem_init
該函式用於建立訊號量,其原型如下:
int sem_init(sem_t *sem, int pshared, unsigned int value);
該函式初始化由 sem 指向的訊號物件,並給它一個初始的整數值 value。
pshared 控制訊號量的型別,值為 0 代表該訊號量用於多執行緒間的同步,值如果大於 0 表示可以共享,用於多個相關程序間的同步
引數 pshared > 0 時指定了 sem 處於共享記憶體區域,所以可以在程序間共享該變數
2.2 sem_wait
int sem_wait(sem_t *sem);
int sem_trywait(sem_t *sem);
sem_wait 是一個阻塞的函式,測試所指定訊號量的值,它的操作是原子的。若 sem value > 0,則該訊號量值減去 1 並立即返回。若sem value = 0,則阻塞直到 sem value > 0,此時立即減去 1,然後返回。
sem_trywait 函式是非阻塞的函式,它會嘗試獲取獲取 sem value 值,如果 sem value = 0,不是阻塞住,而是直接返回一個錯誤 EAGAIN。
2.3 sem_post
把指定的訊號量 sem 的值加 1,喚醒正在等待該訊號量的任意執行緒。
int sem_post(sem_t *sem);
2.4 sem_getvalue
int sem_getvalue(sem_t *sem, int *sval);
獲取訊號量 sem 的當前值,把該值儲存在 sval,若有 1 個或者多個執行緒正在呼叫 sem_wait 阻塞在該訊號量上,該函式返回阻塞在該訊號量上程序或執行緒個數。
2.5 sem_destroy
該函式用於對用完的訊號量的清理。它的原型如下:
int sem_destroy(sem_t *sem);
成功則返回 0,失敗返回 -1
3. 一個訊號量同步執行緒的例子
下面以一個簡單的多執行緒例子說明如何使用訊號量進行執行緒同步。
在主執行緒中,建立一個子執行緒用於處理 resource 共享資源,如果主執行緒有需求(sem_post),就往其後追加一個 ‘a’ 字串。
#include <stdio.h>
#include <semaphore.h>
#include <pthread.h>
#include <unistd.h>
sem_t sem;
void* change_resource(void *res)
{
char *msg = (char*)res;
qDebug("1111");
while (1) {
sem_wait(&sem);
strcat(msg, "a");
qDebug("resource changed with value: %s\n", msg);
}
}
int main()
{
ret = sem_init(&sem, 0, 0);
if (ret == -1) {
printf("sem_init failed \n");
return -1;
}
ret = pthread_create(&thread, NULL, change_resource, (void*)resource);
if (ret != 0) {
printf("pthread_create failed \n");
return -1;
}
while (1) {
sem_post(&sem);
sleep(1);
}
}
資源處理列印如下:
resource changed with value: a
resource changed with value: aa
resource changed with value: aaa
...