线程同步的方式
信号量
linux sem 信号量是一种特殊的变量,访问具有原子性, 用于解决进程或线程间共享资源引发的同步问题。
用户态进程对 sem 信号量可以有以下两种操作:
等待信号量
当信号量值为 0 时,程序等待;当信号量值大于 0 时,信号量减 1,程序继续运行。
发送信号量
将信号量值加 1
通过对信号量的控制,从而实现共享资源的顺序访问。
信号量 和互斥锁的区别在于:互斥锁只允许一个线程进入临界区,信号量允许多个线程同时进入临界区
//原生接口
#include <semaphore.h>
//初始化信号量 pshared指定线程或进程共享,0表示单个进程的线程间共享,1表示进程间共享,value指示初始值
int sem_init(sem_t *sem,int pshared,unsigned int value);
//该函数用于对用完的信号量的清理。成功返回0 否则-1
int sem_destroy(sem_t *sem);
//阻塞等待,测试所指定信号量的值,原子操作。若 sem value > 0,则该信号量值减去 1 并立即返回。
//若sem value = 0,则阻塞直到 sem value > 0,此时立即减去 1,然后返回。
int sem_wait(sem_t *sem);
//sem_wait的非阻塞版本,若 sem value > 0,则该信号量值减去 1 并立即返回。
//若sem value = 0,不是阻塞住,而是直接返回一个错误 EAGAIN。
int sem_trywait(sem_t *sem);
//把指定的信号量 sem 的值加 1,唤醒正在等待该信号量的任意线程。
int sem_post(sem_t *sem);
//获取信号量 sem 的当前值,把该值保存在 sval,若有 1 个或者多个线程正在调用 sem_wait 阻塞在该信号量上,该函数返回阻塞在该信号量上进程或线程个数。
int sem_getvalue(sem_t *sem);
互斥锁
互斥锁保证共享数据操作的完整性。每个对象都对应于一个可称为" 互斥锁" 的标记,这个标记用来保证在任一时刻,只能有一个线程访问该对象。
#include <pthread.h>
//初始化一个互斥体变量,如果参数attr 为NULL,则互斥体变量mutex 使用默认的属性。
int pthread_mutex_init( pthread_mutex_t *mutex, const pthread_mutex_attr_t* attr );
//释放分配给参数mutex 的资源。成功为0,其余不成功
int pthread_mutex_destroy ( pthread_mutex_t *mutex );
//阻塞加锁。如果参数mutex 所指的互斥体已经被锁住了,那么发出调用的线程将被阻塞直到其他线程对mutex 解锁。
int pthread_mutex_lock( pthread_mutex_t *mutex );
//非阻塞加锁。如果该互斥体已经被上锁,该调用不会阻塞等待,而会返回一个错误代码。
int pthread_mutex_trylock( pthread_t *mutex );
//解锁。如果当前线程拥有参数mutex 所指定的互斥体,该调用将该互斥体解锁。
int pthread_mutex_unlock( pthread_mutex_t *mutex );
条件变量
条件变量是线程的另外一种同步机制,这些同步对象为线程提供了会合的场所,理解起来就是两个(或者多个)线程需要碰头(或者说进行交互-一个线程给另外的一个或者多个线程发送消息),我们指定在条件变量这个地方发生,一个线程用于修改这个变量使其满足其它线程继续往下执行的条件,其它线程则接收条件已经发生改变的信号。
条件变量同锁一起使用使得线程可以以一种无竞争的方式等待任意条件的发生。所谓无竞争就是,条件改变这个信号会发送到所有等待这个信号的线程。而不是说一个线程接受到这个消息而其它线程就接收不到了。
#include <pthread.h>
//初始化条件变量 当参数cattr为空指针时,函数创建的是一个缺省的条件变量。否则条件变量的属性将由cattr中的属性值来决定。
int pthread_cond_init(pthread_cond_t *cv,const pthread_condattr_t *cattr);
//阻塞等待条件变量被唤醒。函数将解锁mutex参数指向的互斥锁,并使当前线程阻塞在cv参数指向的条件变量上。
//被阻塞的线程可以被pthread_cond_signal函数,pthread_cond_broadcast函数唤醒,也可能在被信号中断后被唤醒。
int pthread_cond_wait(pthread_cond_t *cv,pthread_mutex_t *mutex);
//唤醒条件变量,解除阻塞 解除在条件变量上的阻塞
int pthread_cond_signal(pthread_cond_t *cv);
//限时等待条件变量被唤醒 函数到了一定的时间,即使条件未发生也会解除阻塞。时间由参数abstime指定。
int pthread_cond_timedwait(pthread_cond_t *cv, pthread_mutex_t *mp, const structtimespec * abstime);
//全部阻塞线程被唤醒,函数唤醒所有被pthread_cond_wait函数阻塞在某个条件变量上的线程,参数cv被用来指定这个条件变量。当没有线程阻塞在这个条件变量上时,pthread_cond_broadcast函数无效。
//由于pthread_cond_broadcast函数唤醒所有阻塞在某个条件变量上的线程,这些线程被唤醒后将再次竞争相应的互斥锁,所以必须小心使用pthread_cond_broadcast函数。
int pthread_cond_broadcast(pthread_cond_t *cv);
//释放条件变量
int pthread_cond_destroy(pthread_cond_t *cv);
封装三类线程同步
#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(int num)
{
if (sem_init(&m_sem, 0, num) != 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;
}
pthread_mutex_t *get()
{
return &m_mutex;
}
private:
pthread_mutex_t m_mutex;
};
/*封装条件变量的类*/
class cond
{
public:
/*创建并初始化条件变量*/
cond()
{
if (pthread_cond_init(&m_cond, NULL) != 0)
{
//pthread_mutex_destroy(&m_mutex);
throw std::exception();
}
}
~cond()
{
pthread_cond_destroy(&m_cond);
}
/*阻塞,等待条件变量*/
bool wait(pthread_mutex_t *m_mutex)
{
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 timewait(pthread_mutex_t *m_mutex, struct timespec t)
{
int ret = 0;
pthread_mutex_lock(&m_mutex);
ret = pthread_cond_timedwait(&m_cond, m_mutex, &t);
pthread_mutex_unlock(&m_mutex);
return ret == 0;
}
/*唤醒一个等待条件变量的线程*/
bool signal()
{
return pthread_cond_signal(&m_cond) == 0;
}
/*广播唤醒等待条件变量的线程*/
bool broadcast()
{
return pthread_cond_broadcast(&m_cond) == 0;
}
private:
//static pthread_mutex_t m_mutex;
pthread_cond_t m_cond;
};
#endif