Posix线程同步(linux)
互斥锁
介绍:
互斥锁,一种信号量,常用来防止多个进程或线程在同一时刻访问相同的共享资源。
头文件:
pthread.h
数据类型:
pthread_mutex_t
初始化:
int pthread_mutex_init (pthread_mutex_t* mutex,const pthread_mutexattr_t* mutexattr); // or pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;
int pthread_mutex_lock(pthread_mutex_t* mutex); //上锁
int pthread_mutex_trylock (pthread_mutex_t* mutex); //如果互斥量没有上锁,返回0,并会对互斥量上锁;如果已经锁,返回EBUSY。
int pthread_mutex_unlock (pthread_mutex_t* mutex); //解锁
int pthread_mutex_destroy (pthread_mutex_t* mutex); //清除互斥锁
函数传入值:
mutex:互斥锁。
mutexattr:
PTHREAD_MUTEX_INITIALIZER 快速互斥锁,默认的类型
PTHREAD_RECURSIVE_MUTEX_INITIALIZER_NP 递归互斥锁,同一线程可以循环上锁
PTHREAD_ERRORCHECK_MUTEX_INITIALIZER_NP 检错互斥锁,如果已经上锁,再上锁会失败而不阻塞,pthread_mutex_lock()操作将会返回EDEADLK。
函数返回值:
成功:0;出错:-1
使用形式:
pthread_mutex_t mutex;
pthread_mutex_init (&mutex, NULL);
pthread_mutex_lock(&mutex); //获取互斥锁
... //临界资源
pthread_mutex_unlock(&mutex); //释放互斥锁
条件变量
介绍:
互斥锁一个明显的缺点是它只有两种状态:锁定和非锁定。设想一种简单情景:多个线程访问同一个共享资源时,并不知道何时应该使用共享资源,如果在临界区里加入判断语句,或者可以有效,但一来效率不高,二来复杂环境下就难以编写了,这是我们需要一个结构,能在条件成立时触发相应线程,进行变量修改和访问。条件变量通过允许线程阻塞和等待另一个线程发送信号的方法弥补了互斥锁的不足,它常和互斥锁一起使用。使用时,条件变量被用来阻塞一个线程,当条件不满足时,线程往往解开相应的互斥锁并等待条件发生变化。一旦其它的某个线程改变了条件变量,它将通知相应的条件变量唤醒一个或多个正被此条件变量阻塞的线程。这些线程将重新锁定互斥锁并重新测试条件是否满足。
头文件:
pthread.h
数据类型:
pthread_cond_t
条件变量的相关函数
pthread_cond_t cond = PTHREAD_COND_INITIALIZER; //条件变量结构
int pthread_cond_init(pthread_cond_t *cond, pthread_condattr_t*cond_attr); //在LinuxThreads中没有实现第二个参数,因此通常为NULL,且被忽略。
int pthread_cond_signal(pthread_cond_t *cond);
int pthread_cond_broadcast(pthread_cond_t *cond);
int pthread_cond_wait(pthread_cond_t *cond, pthread_mutex_t *mutex);
int pthread_cond_timedwait(pthread_cond_t *cond, pthread_mutex_t *mutex, const struct timespec *abstime);
int pthread_cond_destroy(pthread_cond_t *cond);
函数传入值:
cond:条件变量。
cond_attr:通常是NULL
mutex:与条件变量配合使用的互斥锁,传入时应该总是被锁定的状态
函数返回值:
成功:0;出错:-1
pthread_cond_wait ()和pthread_cond_timedwait()都被实现为取消点,因此,在该处等待的线程将立即重新运行,在重新锁定mutex后离开 pthread_cond_wait(),然后执行取消动作。也就是说如果pthread_cond_wait()被取消,mutex是保持锁定状态的,因而需要定义退出回调函数来为其解锁。pthread_cond_wait实际上可以看作是以下几个动作的合体:解锁线程锁;等待条件为true;加锁线程锁。
使用形式:
// 线程一代码
pthread_mutex_lock(&mutex);
if (条件满足) {
pthread_cond_signal(&cond);
}
pthread_mutex_unlock(&mutex);
// 线程二代码
pthread_mutex_lock(&mutex);
while (条件不满足) {
pthread_cond_wait(&cond, &mutex);
}
pthread_mutex_unlock(&mutex);
问题:
1 为什么条件变量要和互斥锁一起用
pthread_cond_wait会先解锁,然后进入 wait cond状态,如果没有配合互斥锁,线程1在调用pthread_cond_wait()还没有进入wait cond的状态的时候,此时线程2调用了 cond_singal。这个cond_singal就丢失了。加了锁的情况是,线程2必须等到 mutex被释放(也就是 pthread_cod_wait() 进入wait_cond状态并自动释放mutex)的时候才能调用cond_singal.(注意,cond signal激发的时候如果没有等待的线程,就会丢失掉)
2 为什么线程2使用while
1, signal、broadcast唤醒到wait返回之间有时间差,防止这个时间差内条件再次被更改为不满足。2 pthread_cond_signal可能唤醒多个线程,如果是多个线程都在等待这个条件,而同时只能有一个线程进行处理,此时就必须要再次条件判断,以使只有一个线程进入临界区处理。
信号灯
介绍:
信号量其实就是一个计数器,也是一个整数。wait相当于P操作(-1),post相当于V操作(+1)。每一次调用wait操作将会使semaphore值减一,而如果semaphore值已经为0,则wait操作将会阻塞,而sem_trywait则会立即返回。每一次调用post操作将会使semaphore值加一,同时发出唤醒的信号给等待的进程(或线程)。
头文件:
semaphore.h
数据类型:
sem_t
相关函数
int sem_init (sem_t* sem, int pshared, unsigned int value); //创建信号量,并初始化值,第二个参数linux没实现,设0
int sem_wait (sem_t* sem); //等待信号量
int sem_trywait (sem_t* sem); //非阻塞等待信号量
int sem_post (sem_t* sem); //发送信号量
int sem_getvalue (sem_t* sem); //得到信号量的值
int sem_destroy (sem_t* sem); //删除信号量
函数传入值:
sem:信号量。
value:初始计算器
函数返回值:
成功:0;出错:-1
使用形式:
sem_t sem;
sem_init(&sem, 0, 1); /*信号量初始化*/
...
sem_wait(&sem); //等待信号量
... //临界资源s
sem_post(&sem); //释放信号量
信号量与线程锁、条件变量相比还有以下几点不同:
1)锁必须是同一个线程获取以及释放,否则会死锁。而条件变量和信号量则不必。
2)信号的递增与减少会被系统自动记住,系统内部有一个计数器实现信号量,不必担心会丢失,而唤醒一个条件变量时,如果没有相应的线程在等待该条件变量,这次唤醒将被丢失。
3)信号灯要比条件变量系统开销大
4)mutex是一把钥匙,一个人拿了就可进入一个房间,出来的时候把钥匙交给队列的第一个。一般的用法是用于串行化对critical section代码的访问,保证这段代码不会被并行的运行。Semaphore是一件可以容纳N人的房间,如果人不满就可以进去,如果人满了,就要等待有人出来。对于N=1的情况,称为binary semaphore。一般的用法是,用于限制对于某一资源的同时访问。
BOOST线程同步
boost互斥量
介绍:
boost提供了7中mutex,功能类似posix中的mutex,仅介绍最常用的boost::mutex
头文件:
boost/thread.hpp
数据类型:
boost::mutex
相关函数
void lock();
bool try_lock();
void unlock();
bool timed_lock(system_time const &abs_time);
使用形式:(不常用)
boost::mutex mu;
try { //必须使用 try-block保证解锁互斥量
mu.lock();
... //临界资源
mu.unlock();
} catch (...) {
mu.unlock();
}
更常用的使用形式:(scopted_lock是mutex类的内部类,用于自动将代码块锁定、解锁。如下:将在进入局部块时锁定,离开时解锁(也可用于 if 和 while 等)
boost::mutex mu;
{
boost::mutex::scopted_lock localLock(mu);
... //临界资源
}
boost条件变量
介绍:
boost提供了两种条件变量对象,一般情况使用condition_variable_any
头文件:
boost/thread.hpp
数据类型:
boost::condition_variable_any
相关函数:
void notify_one();
void notify_all();
void wait(lock_type &lock); //创建mutex对象,上锁或者对其上局部锁,然后传入wait
bool timed_wait(lock_type &lock, duration_type const &rel_time);
使用形式:
boost::mutex mu;
boost::condition_variable_any cond;
// 线程一代码
{
mutex::scopted_lock localLock(mu);
if (条件满足) {
cond.notify_one();
}
}
// 线程二代码
{
mutex::scopted_lock localLock(mu);
while (条件不满足) {
cond.wait(mu);
}
}