[TOC]

信号量(semaphore)是一种轻量级的多线程或多进程同步机制,用来制约多个线程同时对共享资源的 并发访问(控制线程的并发数量)。信号量可以理解为一个计数器,信号量本身的操作不可被打断,是原子操作,必须由操作系统内核来实现信号量。

一、信号量的原理

​ 信号量就是一个计时器。我们以常见的二值信号量来举例,假设该二值信号量为sem,初始值为1,当某个进程/线程想要使用某个共享资源时,该程序先检查该信号量sem:

​ 如果信号量sem==1,表示该程序可以使用该共享资源,其访问共享资源时,信号量-1,相当于对该共享资源加锁。

​ 如果信号量sem==0,表示该程序不可以使用该共享资源,共享资源不可访问,该程序休眠等待,直至信号量sem==1时才能访问(解锁)。

二、C++的信号量工具

​ C++11之后的版本提供了信号量机制,使用时导入头文件#include <semaphore.h>

1、初始化一个信号量

信号量的类型:sem_t

#include <semaphore.h>//信号量
sem_t semId_;//定义一个信号量变量
sem_init(&semId_, 0, 5);//初始化信号量

详细分析下初始化函数:

int sem_init(sem_t *sem,int pshared,unsigned int value);

参数:

sem是要初始化的信号量指针; pshared为0时,信号量sem由进程内线程共享,应该定义为进程内的全局变量或者堆上动态分配的变量 为1表示信号量sem由进程之间共享,应该定义在内存共享区域; value是信号量sem的初始值 返回值:

成功时返回 0;错误时,返回 -1

2、信号量的增减

(1)信号量的减少

函数原型如:int sem_wait(sem_t *sem);

该函数用于以原子操作的方式将信号量加-1,等待sem_post抛出信号来,否则一直阻塞。

调用成功返回0,失败返回-1,并且设置error

// 资源减少1
sem_wait(&semId_);
{
    //对某个共享资源的操作  
}

(2)信号量的增加

该函数原型如: int sem_post(sem_t *sem);

该函数用于以原子操作的方式将信号量加1,并且会抛出信号,原本被阻塞的sem_wait将被打开:

调用成功返回0,失败返回-1,并且设置error。

// 资源增加1
sem_post(&semId_);

3、信号量的销毁

该函数原型如:int sem_destroy(sem_t *sem);

该函数对用完的信号量清理;

调用成功返回0,失败返回-1,并且设置error。

int sem_destroy(sem_t *sem); 

三、互斥量的原理

​ 互斥量(MuteX)是通过锁的机制来实现线程间的同步问题,可以简单理解为一种特殊的信号量,它的值只能是0和1,也就是上锁和解锁两种状态。

四、C++的互斥锁工具

​ C++11通过#include <mutex>提供了互斥量机制,该文件提供4种锁变量:

std::mutex,最基本的 Mutex 类。
std::recursive_mutex,递归 Mutex 类。
std::time_mutex,定时 Mutex 类。
std::recursive_timed_mutex,定时递归 Mutex 类。

​ 上述这4种锁变量,其中基本都有lock()、unlock()和try_lock()等操作,这些操作都需要程序员自行调用。

​ 为了更加方便地对互斥量进行上锁和解锁,该文件又提供了2种更加方便的上锁的类:

std::lock_guard,与 Mutex RAII 相关,方便线程对互斥量上锁。
std::unique_lock,与 Mutex RAII 相关,方便线程对互斥量上锁,但提供了更好的上锁和解锁控制。

1、std::lock_guard 介绍

​ std::lock_gurad 是 C++11 中定义的模板类。定义如下:

template <class Mutex> class lock_guard;

​ lock_gurad 可以方便地对互斥量上锁,在某个lock_gurad对象的声明周期内,它管理的锁会一直保持上锁状态,当声明周期结束之后,它管理的锁会被自动解锁。

​ lock_guard 对象并不负责管理 Mutex 对象的生命周期,lock_guard 对象只是简化了 Mutex 对象的上锁和解锁操作,方便线程对互斥量上锁,即在某个 lock_guard 对象的声明周期内,它所管理的锁对象会一直保持上锁状态;而 lock_guard 的生命周期结束之后,它所管理的锁对象会被解锁。