# 同步,第 2 部分:計算信號量
> 原文:<https://github.com/angrave/SystemProgramming/wiki/Synchronization%2C-Part-2%3A-Counting-Semaphores>
## 什么是計數信號量?
計數信號量包含一個值,并支持兩個操作“等待”和“發布”。 Post 遞增信號量并立即返回。如果計數為零,“等待”將等待。如果計數不為零,則信號量遞減計數并立即返回。
類比是餅干罐中的餅干(或寶箱中的金幣)的計數。在拿餅干之前,請撥打“等待”。如果沒有剩下的 cookie,那么`wait`將不會返回:它將`wait`直到另一個線程通過調用 post 增加信號量。
簡而言之,`post`遞增并立即返回,而如果計數為零,`wait`將等待。在返回之前它將減少計數。
## 如何創建信號量?
本頁介紹了未命名的信號量。不幸的是 Mac OS X 還不支持這些。
首先確定初始值是零還是其他值(例如,數組中剩余空格的數量)。與 pthread 互斥鎖不同,沒有創建信號量的快捷方式 - 使用`sem_init`
```c
#include <semaphore.h>
sem_t s;
int main() {
sem_init(&s, 0, 10); // returns -1 (=FAILED) on OS X
sem_wait(&s); // Could do this 10 times without blocking
sem_post(&s); // Announce that we've finished (and one more resource item is available; increment count)
sem_destroy(&s); // release resources of the semaphore
}
```
## 我可以從不同的線程調用等待和發布嗎?
是!與互斥鎖不同,增量和減量可以來自不同的線程。
## 我可以使用信號量而不是互斥量嗎?
是的 - 雖然信號量的開銷更大。要使用信號量:
* 用一個計數器初始化信號量。
* 用`sem_wait`替換`...lock`
* 用`sem_post`替換`...unlock`
互斥量是一個信號量,它始終是`waits` `posts`
```c
sem_t s;
sem_init(&s, 0, 1);
sem_wait(&s);
// Critical Section
sem_post(&s);
```
## 我可以在信號處理程序中使用 sem_post 嗎?
是! `sem_post`是可以在信號處理程序中正確使用的少數幾個函數之一。這意味著我們可以釋放一個等待線程,該線程現在可以使我們不允許在信號處理程序本身內調用的所有調用(例如`printf`)。
```c
#include <stdio.h>
#include <pthread.h>
#include <signal.h>
#include <semaphore.h>
#include <unistd.h>
sem_t s;
void handler(int signal)
{
sem_post(&s); /* Release the Kraken! */
}
void *singsong(void *param)
{
sem_wait(&s);
printf("I had to wait until your signal released me!\n");
}
int main()
{
int ok = sem_init(&s, 0, 0 /* Initial value of zero*/);
if (ok == -1) {
perror("Could not create unnamed semaphore");
return 1;
}
signal(SIGINT, handler); // Too simple! See note below
pthread_t tid;
pthread_create(&tid, NULL, singsong, NULL);
pthread_exit(NULL); /* Process will exit when there are no more threads */
}
```
注意,健壯的程序不會在多線程程序中使用`signal()`(“多線程進程中的信號()的效果未指定。” - 信號手冊頁);一個更正確的程序需要使用`sigaction`。
## 我怎么知道更多?
閱讀手冊頁:
* [sem_init](http://man7.org/linux/man-pages/man3/sem_init.3.html)
* [sem_wait](http://man7.org/linux/man-pages/man3/sem_wait.3.html)
* [sem_post](http://man7.org/linux/man-pages/man3/sem_post.3.html)
* [sem_destroy](http://man7.org/linux/man-pages/man3/sem_destroy.3.html)
- UIUC CS241 系統編程中文講義
- 0. 簡介
- #Informal 詞匯表
- #Piazza:何時以及如何尋求幫助
- 編程技巧,第 1 部分
- 系統編程短篇小說和歌曲
- 1.學習 C
- C 編程,第 1 部分:簡介
- C 編程,第 2 部分:文本輸入和輸出
- C 編程,第 3 部分:常見問題
- C 編程,第 4 部分:字符串和結構
- C 編程,第 5 部分:調試
- C 編程,復習題
- 2.進程
- 進程,第 1 部分:簡介
- 分叉,第 1 部分:簡介
- 分叉,第 2 部分:Fork,Exec,等等
- 進程控制,第 1 部分:使用信號等待宏
- 進程復習題
- 3.內存和分配器
- 內存,第 1 部分:堆內存簡介
- 內存,第 2 部分:實現內存分配器
- 內存,第 3 部分:粉碎堆棧示例
- 內存復習題
- 4.介紹 Pthreads
- Pthreads,第 1 部分:簡介
- Pthreads,第 2 部分:實踐中的用法
- Pthreads,第 3 部分:并行問題(獎金)
- Pthread 復習題
- 5.同步
- 同步,第 1 部分:互斥鎖
- 同步,第 2 部分:計算信號量
- 同步,第 3 部分:使用互斥鎖和信號量
- 同步,第 4 部分:臨界區問題
- 同步,第 5 部分:條件變量
- 同步,第 6 部分:實現障礙
- 同步,第 7 部分:讀者編寫器問題
- 同步,第 8 部分:環形緩沖區示例
- 同步復習題
- 6.死鎖
- 死鎖,第 1 部分:資源分配圖
- 死鎖,第 2 部分:死鎖條件
- 死鎖,第 3 部分:餐飲哲學家
- 死鎖復習題
- 7.進程間通信&amp;調度
- 虛擬內存,第 1 部分:虛擬內存簡介
- 管道,第 1 部分:管道介紹
- 管道,第 2 部分:管道編程秘密
- 文件,第 1 部分:使用文件
- 調度,第 1 部分:調度過程
- 調度,第 2 部分:調度過程:算法
- IPC 復習題
- 8.網絡
- POSIX,第 1 部分:錯誤處理
- 網絡,第 1 部分:簡介
- 網絡,第 2 部分:使用 getaddrinfo
- 網絡,第 3 部分:構建一個簡單的 TCP 客戶端
- 網絡,第 4 部分:構建一個簡單的 TCP 服務器
- 網絡,第 5 部分:關閉端口,重用端口和其他技巧
- 網絡,第 6 部分:創建 UDP 服務器
- 網絡,第 7 部分:非阻塞 I O,select()和 epoll
- RPC,第 1 部分:遠程過程調用簡介
- 網絡復習題
- 9.文件系統
- 文件系統,第 1 部分:簡介
- 文件系統,第 2 部分:文件是 inode(其他一切只是數據...)
- 文件系統,第 3 部分:權限
- 文件系統,第 4 部分:使用目錄
- 文件系統,第 5 部分:虛擬文件系統
- 文件系統,第 6 部分:內存映射文件和共享內存
- 文件系統,第 7 部分:可擴展且可靠的文件系統
- 文件系統,第 8 部分:從 Android 設備中刪除預裝的惡意軟件
- 文件系統,第 9 部分:磁盤塊示例
- 文件系統復習題
- 10.信號
- 過程控制,第 1 部分:使用信號等待宏
- 信號,第 2 部分:待處理的信號和信號掩碼
- 信號,第 3 部分:提高信號
- 信號,第 4 部分:信號
- 信號復習題
- 考試練習題
- 考試主題
- C 編程:復習題
- 多線程編程:復習題
- 同步概念:復習題
- 記憶:復習題
- 管道:復習題
- 文件系統:復習題
- 網絡:復習題
- 信號:復習題
- 系統編程笑話