# 同步，第 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)