1、POSIX信号量
#include<semaphore.h>
int sem_init(sem_t* sem, int pshared, unsigned int value);
int sem_destroy(sem_t* sem);
int sem_wait(sem_t* sem);
sem_trywait(sem_t* sem);
int sem_post(sem_t* sem);
这组函数的第一个参数sem标识被操作的信号量。下面分别介绍每个函数:
- sem_init函数用于初始化一个未命名信号量。pshared参数指定信号量类型,如果为0,则表示该信号量只在当前进程*享,即局部信号量,否则该信号量可以在多个进程*享;value参数指定信号量初始值。如果一个信号量已经被初始化,则再次初始化则会导致不可预期的结果。
- sem_destroy函数用于销毁一个信号量。如果销毁一个正被其他线程等待的信号量,将导致不可预期的结果。
- sem_wait函数以原子操作方式将信号量值减1,如果信号量为0,则sem_wait函数将被阻塞,直到这个信号量为非0。
- sem_trywait函数与sem_wait函数功能类似,但它为非阻塞的,当信号量值为0时,它将返回-1,并设置errno为EAGAIN。
- sem_post函数以原子操作方式将信号量值加1,如果信号量的值大于0,则其它等待该信号量的线程将被唤醒。
2.1、互斥锁
互斥锁用户保护临界区代码,保证任一时刻只有一个线程对临界区进行独占式访问,这与二进制信号量功能类似。互斥锁相关函数如下:
#include<pthread.h>
int pthread_mutex_init(pthread_mutex_t* mutex, const pthread_mutexattr_t* mutexattr);
int pthread_mutex_destroy(pthread_mutex_t* mutex);
int pthread_mutex_lock(pthread_mutex_t* mutex);
int pthread_mutex_trylock(pthread_mutex_t* mutex);
int pthread_mutex_unlock(pthread_mutex_t* mutex);
- 第一个参数mutex标识要操作的互斥锁。
- pthread_mutex_init函数用于初始化互斥锁,mutexattr参数指定互斥锁的属性,如果设置为NULL,则表示使用默认属性。初始化互斥锁还可以使用另一种方式:
pthread_mutex = PTHREAD_MUTEX_INITIALIZER //将互斥锁的各个字段初始化为0
- pthread_mutex_destroy函数用于销毁互斥锁,销毁一个已经加锁的互斥锁将导致不可预期的结果。
- pthread_mutex_lock函数以原子操作方式给一个互斥锁(普通锁)加锁,如果该互斥锁已经加锁,则函数调用将阻塞,直到该互斥锁被解锁。
- pthread_mutex_trylock与pthread_mutex_lock功能类似,但是是非阻塞的。当目标互斥锁已经被加锁,则返回错误码EBUSY。
- pthread_mutex_unlock函数以原子操作方式给一个互斥锁解锁。
2.2、互斥锁属性
pthread_mutexattr_t结构定义了互斥锁的属性,并且线程库提供一系列的函数来操作该结构。这里不罗列这些函数,只介绍互斥锁较为重要的属性。
- pshared指定是否允许跨进程共享互斥锁,可选值有二:
- PTHREAD_PROCESS_SHARED,互斥锁可以被跨进程共享
- PTHREAD_PROCESS_PRIVATE,互斥锁只能在一个进程*享
2. type指定互斥锁的类型,主要的几个类型如下:
- PTHREAD_MUTEX_NORMAL,普通锁,默认情况也为普通锁。如果一个线程对一个普通锁加锁,则其余的请求该锁的线程将形成一个队列,当拥有该锁的线程解锁后,队列里的线程将按照优先级来获得该锁。这种锁保证了资源分配的公平性,但是也容易引起问题:一个线程如果对一个自己已经加锁的普通锁再次加锁,则引发死锁;对一个已经被其他线程加锁的普通锁解锁,或者对一个已经解锁的普通锁再次解锁,将导致不可预期结果。
- PTHREAD_MUTEX_ERRORCHECK,检错锁。一个线程如果对一个自己已经加锁的检错锁进行加锁,则返回EDEADLK。对一个已经被其他线程加锁的检错锁解锁,或者对一个已经解锁的检错锁再次解锁,则操作将返回EPERM。
- PTHREAD_MUTEX_RECURSIVE,嵌套锁。这种锁允许一个线程在解锁之前多次对它加锁而不发生死锁,不过其他线程如果想要获得该嵌套锁,则该嵌套锁的拥有者必须执行相应次数的解锁操作。对一个已经被其他线程加锁的嵌套锁解锁,或者对一个已经解锁的嵌套锁再次加锁,则会返回EPERM。
3、条件变量
条件变量用于线程之间同步共享数据的值,即当某个共享数据达到某个值的时候,唤醒等待这个共享数据的线程。
#include<pthread.h>
int pthread_cond_init(pthread_cond_t* cond, const pthread_condattr_t* cond_attr);
int pthread_cond_destroy(pthread_cond_t* cond);
int pthread_cond_broadcast(pthread_cond_t* cond);
int pthread_cond_signal(pthread_cond_t* cond);
int pthread_cond_wait(pthread_cond_t* cond, pthread_mutex_t* mutex);
- 函数的第一个参数cond标识要操作的目标条件变量。
- pthread_cond_init函数用于初始化条件变量,cond_attr为条件变量的属性,如果为NULL,则为默认属性。初始化条件变量还可以使用类似互斥锁的另一种方式,使用宏PTHREAD_COND_INITIALIZER,将条件变量的各个字段初始化为0。
- pthread_cond_destroy函数用于销毁条件变量,如果销毁一个正在被等待的条件变量将失败并返回EBUSY。
- pthread_cond_broadcast函数以广播的形式唤醒所有等待该条件变量的线程。
- pthread_cond_signal函数用于唤醒一个等待目标条件变量的线程,具体唤醒哪个线程,取决于线程的优先级和调度策略。如果想唤醒指定的线程,那么可以采用定义全局变量来标识目标线程,然后使用广播的形式唤醒所有线程,线程被唤醒检查全局变量,判断是否自己被唤醒,如果不是则继续等待。
- pthread_cond_wait函数用于等待条件变量,mutex参数是用于保护条件变量的互斥锁,以确保pthread_cond_wait操作的原子性。函数调用前,先对条件变量加锁,函数调用将现程放入等待条件变量的队列中,然后将互斥锁解锁,这样做就保证了pthread_cond_wait开始执行到线程被放入等待队列的这段时间内,条件变量不会被修改。当pthread_cond_wait函数成功返回时,互斥锁mutex将再次被锁上。
4、线程同步机制包装类
ifndef LOCKER_H
#define LOCKER_H
#include<exception>
#include<pthread.h>
#include<semaphore.h>
/*封装信号量*/
class sem
{
public:
/*创建并初始化信号量*/
sem()
{
if(sem_init(&m_sem, 0, 0) != 0)
{
/*构造函数没有返回值,可以通过抛出异常来报告错误*/
throw std::exception();
}
}
/*销毁信号量*/
~sem()
{
sem_destroy(&m_sem);
}
/*等待信号量*/
bool wait()
{
return sem_wait(&m_sem) == 0;
}
/*增加信号量*/
bool post()
{
return sem_post(&m_sem) == 0;
}
private:
sem_t m_sem;
};
/*封装互斥锁*/
class locker
{
public:
/*创建并初始化互斥锁*/
locker()
{
if(pthread_mutex_init(&m_mutex, NULL) != 0)
{
throw std::exception();
}
}
/*销毁互斥锁*/
~locker()
{
pthread_mutex_destroy(&m_mutex);
}
/*获取互斥锁*/
bool lock()
{
return pthread_mutex_lock(&m_mutex) == 0;
}
/*释放互斥锁*/
bool unlock()
{
return pthread_mutex_unlock(&m_mutex) == 0;
}
private:
pthread_mutex_t m_mutex;
};
/*封装条件变量类*/
class cond
{
public:
/*创建并初始化条件变量*/
cond()
{
if(pthread_mutex_init(&m_mutex, NULL) != 0)
{
throw std::exception();
}
if(pthread_cond_init(&m_cond, NULL) != 0)
{
pthread_mutex_destroy(&m_mutex);
throw std::exception();
}
}
/*销毁条件变量*/
~cond()
{
pthread_mutex_destroy(&m_mutex);
pthread_cond_destroy(&m_cond);
}
/*等待条件变量*/
bool wait()
{
int ret = 0;
pthread_mutex_lock(&m_mutex);
ret = pthread_cond_wait(&m_cond, &m_mutex);
pthread_mutex_unlock(&m_mutex);
return ret == 0;
}
/*唤醒等待条件变量的线程*/
bool signal()
{
return pthread_cond_signal(&m_cond) == 0;
}
private:
pthread_mutex_t m_mutex;
pthread_cond_t m_cond;
};
#endif