下面我们要讨论多线程编程环境下用于线程同步的方式,有:POSIX信号量、互斥量、条件变量。
POSIX信号量
POSIX信号量函数有五个如下:
#include<semaphore.h>
int sem_init(sem* sem, int pshared, unsigned int value);
int sem_destroy(sem_t* sem);
int sem_wait(sem_t* sem);
int sem_trywait(sem_t* sem);
int sem_post(sem_t* sem);
sem_int函数用于初始化一个信号量(POSIX支持有名信号量,这里不讨论),pshared如果是0就表示这个信号量是当前进程的局部信号量(其他值的话用于进程共享,不讨论),value指定信号量的初始值。一定要注意不要初始化一个已经初始化的信号量。
sem_desroty函数用于释放信号量,一定要注意不要去destroy一个正在被占用的信号量。
sem_wait将信号量的value减1,如果value是0,sem_wait将被阻塞,直到这个信号量的值非0。
sem_trywait与sem_wait相似,只是它不阻塞,无论value是否是非零,他都返回。如果value是0,他会返回-1并设置errno为EAGAIN。
sem_post函数将信号量加1,当然这个操作是原子操作,如果信号量的值大于0,其他被sem_wait等待的线程将被唤醒。
互斥锁
#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_unlock(pthread_mutex_t* mutex);
int pthread_mutex_trylock(pthread_mutex_t* mutex);
pthread_mutex_t是一个结构体类型,pthread_mutexattr_t结构体定义的是互斥锁属性(如果设置为NULL就是默认属性)。
init函数用来初始化互斥锁,destory函数用来销毁锁(当然得注意不要销毁一个正在被占用的互斥锁),
lock加锁,unlock解锁,当然trylock是非阻塞版本,失败就会返回错误码。
条件变量
#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);
init函数用于初始化一个条件变量,如果cond_attr是NULL,那么就按默认属性初始化
destroy销毁条件变量,如果条件变量正在被使用则返回EBUSY错误码(和前两个不同).
broadcast唤醒所有等待的线程,signal唤醒一个,至于是哪一个取决于线程的优先级和调度策略.
wait函数调用前一定要确保 mutex上锁,wait函数的调用会先将调用线程放入条件变量的等待队列中,然后将互斥量解锁.显然wait函数执行将调用线程放入条件变量的等待队列中这个过程, signal,broadcast函数不会修改条件变量,换一句话说就是wait函数不会错过条件变量的任何变化. wait函数返回成功后mutex会被继续上锁.
生产者消费者模型
下面是关于条件变量的测试代码,条件变量结合互斥锁经常被用到生产者和消费者模型中,
这里要注意的是条件变量最好放在while里面,下面例子可以用if代替.至于为什么放在while里面比较好,读者可以自己研究.
#include <pthread.h>
#include <list>
#include <stdio.h>
#include <unistd.h>
using namespace std;
pthread_mutex_t list_mutex = PTHREAD_MUTEX_INITIALIZER;//静态初始化
pthread_cond_t cond_em = PTHREAD_COND_INITIALIZER;
list<long> my_list;
void* consume(void* arg)
{
while(1)
{
pthread_mutex_lock(&list_mutex);
while(my_list.empty())
{
pthread_cond_wait(&cond_em, &list_mutex);
}
printf("value = %d\n", my_list.back());
my_list.pop_back();
pthread_mutex_unlock(&list_mutex);
sleep(1);
}
}
void* produce(void* arg)
{
long temp = (long)arg;
while(1)
{
pthread_mutex_lock(&list_mutex);
my_list.push_back(temp);
pthread_mutex_unlock(&list_mutex);
pthread_cond_signal(&cond_em);
sleep(1);
}
}
int main()
{
int i = 0;
pthread_t* pid = new pthread_t[10];
for(; i < 5; ++i)
{
pthread_create(pid+i, NULL, &produce, (void*)i);
}
for(; i < 10; ++i)
{
pthread_create(pid+i, NULL, &consume, (void*)i);
}
--i;
sleep(10);
for(; i>=0; --i)
{
pthread_join(pid[i], NULL);
}
return 0;
}