[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 的生命周期结束之后,它所管理的锁对象会被解锁。