做微商海报的网站,活动推广方式,摄影网站 蜂鸟,wordpress免费商城#x1f57a;作者#xff1a; 主页 我的专栏C语言从0到1探秘C数据结构从0到1探秘Linux菜鸟刷题集 #x1f618;欢迎关注#xff1a;#x1f44d;点赞#x1f64c;收藏✍️留言 #x1f3c7;码字不易#xff0c;你的#x1f44d;点赞#x1f64c;收藏❤️关注对我真的… 作者 主页 我的专栏C语言从0到1探秘C数据结构从0到1探秘Linux菜鸟刷题集 欢迎关注点赞收藏✍️留言 码字不易你的点赞收藏❤️关注对我真的很重要有问题可在评论区提出感谢阅读 文章目录 前言1 相关概念1.1 条件变量1.2 同步概念与竞态条件1.3 条件变量函数 2 实际应用见见猪跑2.1 模拟加锁未加条件变量小迷给小芒煮饭且只有一个碗2.2 模拟加锁且加上条件变量2.3 模拟加锁且加条件变量小迷给多个人做饭 只有一个碗 3 条件变量关于等待接口的几个问题3.1 条件变量对的等待接口参数为什么需要互斥锁3.2 pthread_cond_wait函数的实现原理3.3 线程等待的时候被唤醒之后需要做什么事 前言
当谈到多线程编程时线程同步是一个至关重要的话题。在多线程环境中我们需要确保不同线程之间的数据访问和操作能够正确、有序地进行以避免出现竞争条件和数据不一致的情况。因此线程同步成为了保障多线程程序正确性和可靠性的重要手段。
在本篇博客中我将深入探讨线程同步的概念、原理和常用的同步机制帮助读者更好地理解多线程编程中的挑战和解决方案。无论是初学者还是有一定经验的开发人员都可以通过本文获得对线程同步的全面了解并学习如何在实际项目中应用这些技术来确保多线程程序的稳定性和性能。
让我们一起深入研究线程同步探索其中的奥秘为多线程编程的世界增添一抹精彩的色彩。
1 相关概念
1.1 条件变量
当一个线程互斥的访问某个变量时它可能发现在其他线程改变状态之前它什么也做不了例如一个线程访问队列时发现队列为空它只能等待只到其他线程将一个节点添加到队列中这种情况就需要用到条件变量
1.2 同步概念与竞态条件
同步在保证数据安全的前提下让线程能够按照某种特定顺序访问临界资源从而有效避免饥饿问题这叫做同步。竞态条件因为时序问题而导致程序异常。我们称之为竞态条件。在线程场景下这种问题也不难理解
1.3 条件变量函数
初始化
动态初始化
int pthread_cond_init(pthread_cond_t *restrict cond,const pthread_condattr_t *restrictattr); ❓
参数cond要初始化的条件变量attrNULL
静态初始化
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互斥量之前的博客解释过
作用谁调用该接口就将谁放入PCB等待队列中唤醒等待
唤醒PCB等待队列当中的所有线程int pthread_cond_broadcast(pthread_cond_t *cond);
唤醒PCB等待队列当中至少一个线程int pthread_cond_signal(pthread_cond_t *cond);2 实际应用见见猪跑
2.1 模拟加锁未加条件变量小迷给小芒煮饭且只有一个碗
代码如下
#includeiostream
#includepthread.h
#includeunistd.h
using namespace std;int g_bowl1;
pthread_mutex_t g_lock;void* Eat(void* arg){(void)arg;while(1){pthread_mutex_lock(g_lock);g_bowl--;coutI am pthread_self()I eat g_bowlendl;pthread_mutex_unlock(g_lock);}return NULL;
}void* MakeRice(void* arg){(void)arg;while(1){pthread_mutex_lock(g_lock);g_bowl;coutI am pthread_self()I make g_bowlendl;pthread_mutex_unlock(g_lock);}return NULL;
}int main(){pthread_mutex_init(g_lock,NULL);pthread_t tid_eat;pthread_t tid_make;int ret pthread_create(tid_eat,NULL,Eat,NULL);if(ret0){coutthread create failedendl;}ret pthread_create(tid_make,NULL,MakeRice,NULL);if(ret 0){coutthread create failedendl;}while(1){sleep(1);}pthread_mutex_destroy(g_lock);return 0;
}结果 可以观察到bowl已经减为负数这是因为小芒负责吃当小芒拿到CPU的资源时即使碗里面没有饭它还是持续吃饭最后居然出现了没有饭还能吃饭的情况这显然是不合理的所以需要一个条件变量来控制能否吃以及能否做
2.2 模拟加锁且加上条件变量
给小迷加上条件变量bowl 里面有饭就不做饭给小芒加上条件变量bowl 没有饭就不吃饭。 代码如下
#includeiostream
#includepthread.h
#includeunistd.h
using namespace std;int g_bowl1;
pthread_mutex_t g_lock;pthread_cond_t g_eat_cond;
pthread_cond_t g_make_cond;void* Eat(void* arg){(void)arg;while(1){pthread_mutex_lock(g_lock);if(g_bowl0){pthread_cond_wait(g_eat_cond,g_lock);//等待小迷做好饭}g_bowl--;coutI am pthread_self()I eat g_bowlendl;pthread_mutex_unlock(g_lock);pthread_cond_signal(g_make_cond);//通知小迷做饭}return NULL;
}void* MakeRice(void* arg){(void)arg;while(1){pthread_mutex_lock(g_lock);if(g_bowl1){pthread_cond_wait(g_make_cond,g_lock);//等待小芒吃饭 空出碗}g_bowl;coutI am pthread_self()I make g_bowlendl;pthread_mutex_unlock(g_lock);pthread_cond_signal(g_eat_cond);//通知小芒吃饭}return NULL;
}int main(){pthread_mutex_init(g_lock,NULL);pthread_cond_init(g_eat_cond,NULL);pthread_cond_init(g_make_cond,NULL);pthread_t tid_eat;pthread_t tid_make;int ret pthread_create(tid_eat,NULL,Eat,NULL);if(ret0){coutthread create failedendl;}ret pthread_create(tid_make,NULL,MakeRice,NULL);if(ret 0){coutthread create failedendl;}while(1){sleep(1);}pthread_mutex_destroy(g_lock);pthread_cond_destroy(g_eat_cond);pthread_cond_destroy(g_make_cond);return 0;
}结果如下
2.3 模拟加锁且加条件变量小迷给多个人做饭 只有一个碗
#includeiostream
#includepthread.h
#includeunistd.h
using namespace std;int g_bowl1;
pthread_mutex_t g_lock;pthread_cond_t g_eat_cond;
pthread_cond_t g_make_cond;void* Eat(void* arg){(void)arg;while(1){pthread_mutex_lock(g_lock);if(g_bowl0){pthread_cond_wait(g_eat_cond,g_lock);}g_bowl--;coutI am pthread_self()I eat g_bowlendl;pthread_mutex_unlock(g_lock);pthread_cond_signal(g_make_cond);}return NULL;
}void* MakeRice(void* arg){(void)arg;while(1){pthread_mutex_lock(g_lock);if(g_bowl0){pthread_cond_wait(g_make_cond,g_lock);}g_bowl;coutI am pthread_self()I make g_bowlendl;pthread_mutex_unlock(g_lock);pthread_cond_signal(g_eat_cond);}return NULL;
}int main(){pthread_mutex_init(g_lock,NULL);pthread_cond_init(g_eat_cond,NULL);pthread_cond_init(g_make_cond,NULL);pthread_t tid_make;int ret pthread_create(tid_make,NULL,MakeRice,NULL);if(ret 0){coutthread create failedendl;}for(int i0;i3;i){pthread_t tid_eat;int ret pthread_create(tid_eat,NULL,Eat,NULL);if(ret0){coutthread create failedendl;}}while(1){sleep(1);}pthread_mutex_destroy(g_lock);pthread_cond_destroy(g_eat_cond);pthread_cond_destroy(g_make_cond);return 0;
}结果 可以看到出现了负数的情况这是为什么 这是因为我们是使用if语句来判断条件的可能线程刚好在这个时候进行了切换导致多个eat线程拿到了锁从而发生了这样的现象想要解决这个问题只需要改为while语句即可
#includeiostream
#includepthread.h
#includeunistd.h
using namespace std;int g_bowl1;
pthread_mutex_t g_lock;pthread_cond_t g_eat_cond;
pthread_cond_t g_make_cond;void* Eat(void* arg){(void)arg;while(1){pthread_mutex_lock(g_lock);while(g_bowl0){pthread_cond_wait(g_eat_cond,g_lock);}g_bowl--;coutI am pthread_self() I eat g_bowlendl;pthread_mutex_unlock(g_lock);pthread_cond_signal(g_make_cond);}return NULL;
}void* MakeRice(void* arg){(void)arg;while(1){pthread_mutex_lock(g_lock);while(g_bowl0){pthread_cond_wait(g_make_cond,g_lock);}g_bowl;coutI am pthread_self() I make g_bowlendl;pthread_mutex_unlock(g_lock);pthread_cond_signal(g_eat_cond);}return NULL;
}int main(){pthread_mutex_init(g_lock,NULL);pthread_cond_init(g_eat_cond,NULL);pthread_cond_init(g_make_cond,NULL);pthread_t tid_make;int ret pthread_create(tid_make,NULL,MakeRice,NULL);if(ret 0){coutthread create failedendl;}for(int i0;i3;i){pthread_t tid_eat;int ret pthread_create(tid_eat,NULL,Eat,NULL);if(ret0){coutthread create failedendl;}}while(1){sleep(1);}pthread_mutex_destroy(g_lock);pthread_cond_destroy(g_eat_cond);pthread_cond_destroy(g_make_cond);return 0;
}结果
3 条件变量关于等待接口的几个问题
3.1 条件变量对的等待接口参数为什么需要互斥锁
在pthread_cond_wait函数的内部需要释放互斥锁。释放之后其他线程就可以正常加锁操作了。 eg就像之前小芒发现碗里面没有饭则需要将自己放到PCB等待队列中调用了pthread_cond_wait函数之后需要将拿到互斥锁释放掉小迷就可以访问到碗这个临界资源开始做饭。
3.2 pthread_cond_wait函数的实现原理
在pthread_cond_wait函数内部是先释放互斥锁还是先将PCB放到等待队列中呢 假设先释放互斥锁此时可能做饭的小迷就已经将饭做好了但是小芒还没有到等待队列中小迷通知小芒吃饭但是发现等待队列中为空但是同时发现碗里面有饭它就会将自己放入等待队列中等待此时小芒也才将自己放入等待队列中那么此时小迷和小芒就都在等待队列中进行等待所以不能先释放互斥锁。
3.3 线程等待的时候被唤醒之后需要做什么事
移动出PCB等待队列抢互斥锁 抢到了pthread_cond_wait函数返回了没抢到pthread_cond_wait函数没有返回等待抢锁