信号量的含义
信号量是一个有整数值的对象,它的语义针对某个特定的资源,它的值是可用的资源个数的抽象。
当值大于零时,值表示可用的资源个数。
当值小于零时,值表示正在等待的线程个数。
当值等于零时,表示没有线程等待,但是自己无法使用该资源。
信号量的底层是利用锁与条件变量实现的,而锁与条件变量的实现又是利用到了底层硬件的支持,包括同步原语,等待队列,休眠机制等
信号量的API
信号量最大的好处就是代码量小,尤其是比条件变量小的多,使用极其方便。
//头文件
#include <semaphore.h>
//数据结构
sem_t s;
//初始化,第三个参数表明s的初始值,第二个参数一般设置为0,
//表示信号量是多个线程共享的。
sem_init(&s, 0, 1);
//使用
sem_post(&s);
sem_wait(&s);
sem_wait执行步骤:
- 将信号量的值减1
- 如果此时信号量的值大于等于0,跳出,否则陷入休眠
sem_post执行步骤:
- 将信号量的值加1
- 如果有其他线程休眠,唤醒一个线程
用信号量实现锁
sem_t s;
sem_init(&s, 0, 1);
sem_wait(&s);
//临界区代码
sem_post(&s);
使用一目了然,注意这里信号量的初值赋为1。表示第一个线程可以进入临界区。
用信号量实现条件变量
以父线程等待子线程执行完毕为例。
sem_t s;
void* thr_son(void*)
{
printf("son begin\n");
printf("son end\n");
sem_post(&s);
return NULL;
}
int main()
{
sem_init(&s, 0, 0);
printf("father begin\n");
pthread_t son;
pthread_create(&son, NULL, thr_son, NULL);
sem_wait(&s);
printf("father end\n");
}
使用锁与条件变量实现信号量
这里我们用锁与条件变量实现一个自己的信号量 zem_t。
依次实现信号量的三个API,并用它实现父线程等待子线程的功能。
#include <pthread.h>
#include <stdio.h>
#include <stdlib.h>
typedef struct zem_t{
int value;
pthread_mutex_t mutex;
pthread_cond_t cond;
}zem_t;
zem_t z;
void zem_init(zem_t* zem_p, int value)
{
zem_p->value = value;
pthread_mutex_init(&zem_p->mutex, NULL);
pthread_cond_init(&zem_p->cond, NULL);
}
void zem_post(zem_t* zem_p)
{
pthread_mutex_lock(&zem_p->mutex);
zem_p->value++;
pthread_cond_signal(&zem_p->cond);
pthread_mutex_unlock(&zem_p->mutex);
}
void zem_wait(zem_t* zem_p)
{
pthread_mutex_lock(&zem_p->mutex);
zem_p->value--;
while (zem_p->value < 0)
pthread_cond_wait(&zem_p->cond, &zem_p->mutex);
pthread_mutex_unlock(&zem_p->mutex);
}
void* thr_son(void*)
{
printf("son begin\n");
printf("son end\n");
zem_post(&z);
return NULL;
}
int main()
{
zem_init(&z, 0);
printf("father begin\n");
pthread_t son;
pthread_create(&son, NULL, thr_son, NULL);
zem_wait(&z);
printf("father end\n");
}