2. 采用读者优先策略或者当前没有写锁申请请求,即 _nr_writers_queue = 0
3. 当满足这两个条件时,读锁请求立即获得读锁,返回之前执行_nr_readers++,表示多了一个线程正在读
4. 不满足这两个条件时,执行_nr_readers_queued++,表示增加了一个读锁等待者,然后调用futex,陷入阻塞。醒来之后,执行_nr_readers_queued- -,再次判断是否满足条件1,2
对于写请求而言:如果
1. 无线程持有写锁,即_writer = 0.
2. 没有线程持有读锁,即_nr_readers = 0.
3. 如果上述条件满足,就会立即拿到锁,将_writer 置为当前线程的ID
4. 如果不满足,则执行_nr_writers_queue++, 表示增加了一个写锁等待者线程,然后执行futex陷入等待。醒来后,先执行_nr_writers_queue- -,再继续判断条件1,2
对于解锁,如果当前是写锁:
1. 执行_writer = 0.,表示释放写锁。
2. 根据_nr_writers_queue判断有没有写锁,如果有则唤醒一个写锁,如果没有写锁等待者,则唤醒所有的读锁等待者。
对于解锁,如果当前是读锁:
1. 执行_nr_readers- -,表示读锁占有者少了一个。
2. 判断_nr_readers是否等于0,是的话则表示当前线程是最后一个读锁占有者,需要唤醒写锁等待者或读锁等待者
3. 根据_nr_writers_queue判断是否存在写锁等待者,若有,则唤醒一个写锁等待线程
4. 如果没有写锁等待者,判断是否存在读锁等待者,若有,则唤醒全部的读锁等待者
读写锁很容易造成,读者饿死或者写者饿死。
也可以设计公平的读写锁。
代码:
#include #include #include #include #include #include #define THREADCOUNT 100static int count = 0;static pthread_rwlock_t lock;void* Read(void* i){ while(1) { pthread_rwlock_rdlock(&lock); printf("i am 读线程 : %d, 现在的count是%dn", (int)syscall(SYS_gettid), count); pthread_rwlock_unlock(&lock); //sleep(1); }}void* Write(void* i){ while(1) { pthread_rwlock_wrlock(&lock); ++count; printf("i am 写线程 : %d, 现在的count是: %dn", (int)syscall(SYS_gettid), count); pthread_rwlock_unlock(&lock); sleep(1); }}int main(){ //close(1); //int fd = open("./dup2_result.txt", O_CREAT | O_RDWR); //dup2(fd, 1); pthread_t tid[THREADCOUNT]; pthread_rwlock_init(&lock, NULL); for(int i = 0; i < THREADCOUNT; ++i) { if(i % 2 == 0) { pthread_create(&tid[i], NULL, Read, (void*)&i); } else { pthread_create(&tid[i], NULL, Write, (void*)&i); } } for(int i = 0; i < THREADCOUNT; ++i) { pthread_join(tid[i], NULL); } pthread_rwlock_destroy(&lock); return 0;}
上述代码很容易触发线程饿死。
读饿死或者写饿死。
7.线程间同步
7.1为什么需要线程同步?
线程同步是为了对临界资源访问的合理性。
例如:
就像工厂里生产车间没有原料了, 所有生产车间都停工了, 工人们都在车间睡觉。 突然进来一批原料, 如果原料充足, 你会发广播给所有车间, 原料来了, 快来开工吧。 如果进来的原料很少, 只够一个车间开工的, 你可能只会通知一个车间开工。
7.2如何做到线程间同步?
条件等待是线程间同步的另一种方法。
如果条件不满足, 它能做的事情就是等待, 等到条件满足为止。 通常条件的达成, 很可能取决于另一个线程, 比如生产者-消费者模型。 当另外一个线程发现条件符合的时候, 它会选择一个时机去通知等待在这个条件上的线程。 有两种可能性, 一种是唤醒一个线程, 一种是广播, 唤醒其他线程。
则在这个情况下,需要做到:
1、线程在条件不满足的情况下, 主动让出互斥量, 让其他线程去折腾, 线程在此处等待, 等待条件的满足;
2、一旦条件满足, 线程就可以立刻被唤醒。
3、线程之所以可以安心等待, 依赖的是其他线程的协作, 它确信会有一个线程在发现条件满足以后, 将向它发送信号, 并且让出互斥量。
7.3条件变量
本质上是PCB等待队列 + 等待接口 + 唤醒接口。
7.3.1条件变量的初始化
静态初始化
pthread_cond_t cond = PTHREAD_COND_INITIALIZER;
动态初始化
pthread_cond_init(pthread_cond_t *cond,const pthread_condattr_t *attr);