同步与互斥(同步)
线程同步
条件变量
- 当⼀个线程互斥地访问某个变量时,它可能发现在其它线程改变状态之前,它什么也做不了。
- 例如⼀个线程访问队列时,发现队列为空,它只能等待,只到其它线程将⼀个节点添加到队列中。这种情况就需要⽤到条件变量。
同步概念与竞态条件
- 同步:在保证数据安全的前提下,让线程能够按照某种特定的顺序访问临界资源,从⽽有效避免 饥饿问题,叫做同步
- 竞态条件:因为时序问题,⽽导致程序异常,我们称之为竞态条件。在线程场景下,这种问题也不难理解
总结:
- 互斥能保证安全性,但是安全不一定合理和高效. 用互斥量来实现互斥
- 同步在安全的前提下 ,让系统变得合理和高效 用条件变量来实现同步
- 条件变量= 通知 + 等待队列
- 条件变量能够使线程同步 ,用对列维护线程顺序
条件变量函数
初始化
方法1: 这种初始化使用的是局部条件变量 ,后面需要销毁
int pthread_cond_init(pthread_cond_t *restrict cond,const pthread_condattr_t *restrict attr);参数:cond:要初始化的条件变量attr:NULL
方法2: 使用全局条件变量, 后续不用销毁了 ,类似互斥量
pthread_cond_t cond = PTHREAD_COND_INITIALIZER;
销毁
int pthread_cond_destroy(pthread_cond_t *cond)
线程等待条件满足 ,不满足就在等待对列中阻塞(在此函数中阻塞)
int pthread_cond_wait(pthread_cond_t *restrict cond,pthread_mutex_t *restrict
mutex);参数:cond:要在这个条件变量上等待mutex:互斥量,后⾯详细解释(等的时候会自动释放传来的锁 ,进入函数内部阻塞 ,唤醒时会自动持有锁)
唤醒等待队列中的线程
int pthread_cond_broadcast(pthread_cond_t *cond); 唤醒等待对列中全部的线程int pthread_cond_signal(pthread_cond_t *cond); 唤醒等待对列中第一个线程
简单案例:
我们先使⽤PTHREAD_COND/MUTEX_INITIALIZER进⾏测试,对其他细节暂不追究
然后将接⼝更改成为使⽤ pthread_cond_init / pthread_cond_destroy 的⽅式, ⽅便后 续进⾏封装
#include <iostream>
#include <cstdio>
#include <string>
#include <pthread.h>
#include <unistd.h>pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;
pthread_cond_t cond = PTHREAD_COND_INITIALIZER;void *active(void *arg)
{std::string name = static_cast<const char *>(arg);while (true){pthread_mutex_lock(&mutex);// 没有对于资源是否就绪的判定pthread_cond_wait(&cond, &mutex); // mutex??printf("%s is active!\n", name.c_str());pthread_mutex_unlock(&mutex);}
}int main()
{pthread_t tid1, tid2, tid3;pthread_create(&tid1, nullptr, active, (void *)"thread-1");pthread_create(&tid2, nullptr, active, (void *)"thread-2");pthread_create(&tid3, nullptr, active, (void *)"thread-3");sleep(1);printf("Main thread ctrl begin...\n");while (true){printf("main wakeup thread...\n");// pthread_cond_signal(&cond);pthread_cond_broadcast(&cond);sleep(1);}pthread_join(tid1, nullptr);pthread_join(tid2, nullptr);pthread_join(tid3, nullptr);
}