线程可以使用的同步机制包括互斥量和条件变量、读写锁,条件变量是一种相对不太好懂的机制。
先给出pthread对条件变量的关键API:
pthread_cond_wait(pthread_cond_t &cond,pthread_mutex_t &lock); 线程等待条件cond的变化;
pthread_cond_signal(pthread_cond_t &cond);线程告诉等待cond的线程条件已经改变。
条件变量本身也是由互斥量来保护的,具体的原理大致是:
考虑这个情况:线程进到一个临界区比如说链表,这时发现链表是空的,没什么事情可以做(即条件不满足),那么就先进入一个等待条件队列,去等待条件的发生;而另一个线程对这个临界资源链表进行添加操作,那么说明链表不空了,即条件满足,这时候这个线程需要通知等待队列里的等待线程告诉他们条件满足了,之后等待条件队列里的线程就继续运行。
用pthreadAPI来说明就是
1线程A先进入临界资源链表,先上锁mutex,然后调用pthread_cond_wait(&cond,&lock)来等待条件的改变,pthread_cond_wait传入的互斥量必须是已经上锁的,在这个函数中做两件事,1.解锁这个互斥量;2.得到条件改变以后再加锁互斥量后返回。
当Phread_cond_wait解锁互斥量之后,线程B就可以从阻塞队列中恢复(因为A之前已经上锁了互斥量,所以B先阻塞),对这个互斥量上锁,然后操作临界资源,操作完之后调用pthread_cond_signal(&cond),即向等待本条件改变的线程发送信号告诉线程条件已经改变;那么A中的pthread_cond_wait得到信号,然后准备再把互斥量上锁,但是由于此时线程B仍对本互斥量上锁,所以要等到B线程释放本互斥量,然后再pthread_cond_wait对互斥量再次上锁,之后返回,然后线程A便可以操作满足资源后的临界资源。
这里我写了一个样例程序:线程1对count进行+1操作,线程2在每次count+1后读取count值打印。
利用条件变量的思路:
线程2先执行,上锁,然后等待条件,此时等待条件函数中已经解锁,于是线程1可以运行,上锁,count+1,然后发送信号告诉等待条件的线程条件已经满足,之后线程1释放锁;那么线程2的等待条件函数得到条件改变,而且锁已经被释放,之后上锁,返回线程2继续运行,打印count,之后释放锁;于是等待锁的线程1再次运行,+1。。。
#include <stdio.h>
#include <pthread.h>
#include <stdlib.h>
#include <unistd.h>
int count=0;
struct Lockcond{
pthread_mutex_t lock;
pthread_cond_t cond;
};
typedef struct Lockcond lockcond;
void *rtn1(void *arg);
void *rtn2(void *arg);
void main(){
pthread_t tid1,tid2;
lockcond *lcp=(lockcond*)malloc(sizeof(lockcond));
pthread_mutex_init(&lcp->lock,NULL);
pthread_cond_init(&lcp->cond,NULL);
int err;
if((err=pthread_create(&tid2,NULL,rtn1,(void *)lcp))!=0)
perror("create thread 1 wrong");
if((err=pthread_create(&tid1,NULL,rtn2,(void *)lcp))!=0)
perror("create thread 2 wrong");
if((err=pthread_join(tid2,NULL))!=0)
perror("exit thread 1 wrong");
if((err=pthread_join(tid1,NULL))!=0)
perror("exit thread 2 wrong");
pthread_mutex_destroy(&lcp->lock);
pthread_cond_destroy(&lcp->cond);
}
void *rtn1(void *arg){
sleep(1);
lockcond *lcp=(lockcond *)arg;
int i=0;
for(;i<10;i++){
printf("thread 1 going to lock\n");
pthread_mutex_lock(&lcp->lock);
printf("thread 1 lock\n");
count++;
printf("thread 1 send signal\n");
pthread_cond_signal(&lcp->cond);
printf("thread 1 send ok\n");
pthread_mutex_unlock(&lcp->lock);
printf("thread 1 unlock\n");
sleep(1); //这么做是为了保证每次都是线程2先占有锁
}
}
void *rtn2(void *arg){
printf("coming in thread 2\n");
lockcond *lcp=(lockcond *)arg;
int i=0;
while(i++<10){
pthread_mutex_lock(&lcp->lock);
printf("thread 2 wait\n");
pthread_cond_wait(&lcp->cond,&lcp->lock);
printf("thread 2 wait ok\n");
printf("Thread 2 print count=%d\n",count);
pthread_mutex_unlock(&lcp->lock);
}
}
运行结果:
coming in thread 2 先进入线程2
thread 2 wait 此时线程2已经经历了上锁,然后调用等待函数,解锁,且正在等待条件的改变
thread 1 going to lock 线程1准备上锁
thread 1 lock 线程1已经上锁
thread 1 send signal 完成+1操作然后发信好
thread 1 send ok 发送信号完成
thread 1 unlock 解锁
thread 2 wait ok 这时 线程2等待可以返回,再次上锁
Thread 2 print count=1 打印,之后解锁
thread 2 wait 此时线程2又已经经历了上锁,然后调用等待函数,解锁,且正在等待条件的改变
thread 1 going to lock
thread 1 lock
thread 1 send signal
thread 1 send ok
thread 1 unlock
thread 2 wait ok
Thread 2 print count=2
thread 2 wait
thread 1 going to lock
thread 1 lock
thread 1 send signal
thread 1 send ok
thread 1 unlock
thread 2 wait ok
Thread 2 print count=3
thread 2 wait
thread 1 going to lock
thread 1 lock
thread 1 send signal
thread 1 send ok
thread 1 unlock
thread 2 wait ok
Thread 2 print count=4
thread 2 wait
thread 1 going to lock
thread 1 lock
thread 1 send signal
thread 1 send ok
thread 1 unlock
thread 2 wait ok
Thread 2 print count=5
thread 2 wait
thread 1 going to lock
thread 1 lock
thread 1 send signal
thread 1 send ok
thread 1 unlock
thread 2 wait ok
Thread 2 print count=6
thread 2 wait
thread 1 going to lock
thread 1 lock
thread 1 send signal
thread 1 send ok
thread 1 unlock
thread 2 wait ok
Thread 2 print count=7
thread 2 wait
thread 1 going to lock
thread 1 lock
thread 1 send signal
thread 1 send ok
thread 1 unlock
thread 2 wait ok
Thread 2 print count=8
thread 2 wait
thread 1 going to lock
thread 1 lock
thread 1 send signal
thread 1 send ok
thread 1 unlock
thread 2 wait ok
Thread 2 print count=9
thread 2 wait
thread 1 going to lock
thread 1 lock
thread 1 send signal
thread 1 send ok
thread 1 unlock
thread 2 wait ok
Thread 2 print count=10