欢迎来到Cefler的博客😁
🕌博客主页:折纸花满衣
🏠个人专栏:题目解析
目录
- 👉🏻 完成两个线程通过条件变量实现交替打印
- 错误代码加优化(c++线程库版本)
- 版本2(使用phtread.h库)
- 👉🏻按序打印
- 👉🏻 H2O 生成
👉🏻 完成两个线程通过条件变量实现交替打印
1.题目描述:线程A打印-我是线程A;线程B打印-我是线程B; 最终实现交替打印,不能出现连续的相同打印。
2.本题主要考察条件变量的基本使用流程
错误代码加优化(c++线程库版本)
#include<iostream>
#include<mutex>
#include<thread>
#include<condition_variable>
#include<unistd.h>using namespace std;
mutex mx;//互斥锁
condition_variable cv;//条件变量bool flag = true;
void Print(char args)
{char c = static_cast<char>(args);for (int i = 0; i < 5; i++){unique_lock<mutex> lock(mx); // 上锁//if(!flag)cv.wait(lock,[]{return flag;}); // 等待条件 flag 为 true[]{return flag||!flag;}if(c=='A')cout << "我是线程A" << endl;else if(c=='B')cout<<"我是线程B"<<endl;flag = !flag;cv.notify_one(); // 通知另一个线程sleep(1);}
}
int main()
{//创建AB线程std::thread threadA(Print,'A');std::thread threadB(Print,'B');// 等待线程结束threadA.join();threadB.join();return 0;
}
此代码会导致第一次打印字符A后就阻塞不动了,
主要原因出在这行代码上
cv.wait(lock,[]{return flag;});
wait的第二个参数是个函数对象(这里是lamda表达式),当返回值是false时,wait会进行阻塞,true时,wait会开始释放锁,当前线程拿到锁后开始继续执行。
因为flag一开始为true,所以此时线程A是拿到锁并执行打印工作的。
而后将flag改为false,并去唤醒线程B(唤醒它来看看wait是否满足情况了),此时第二次循环进来,线程A会被wait阻塞住。
而线程B这边已经苏醒,过来第一次循环,结果被阻塞,这是怎么回事?因为flag被改为false了,所以线程B阻塞了,而此时线程A同时也被阻塞,直接导致两个线程统统被阻塞,所以这个wait的判定条件是个大问题。
要解决的话就是将线程A和线程B进来的不同情况都要考虑进去。
代码优化如下:
#include<iostream>
#include<mutex>
#include<thread>
#include<condition_variable>
#include<unistd.h>using namespace std;
mutex mx;//互斥锁
condition_variable cv;//条件变量bool flag = true;
void Print(char args)
{char c = static_cast<char>(args);for (int i = 0; i < 5; i++){unique_lock<mutex> lock(mx); // 上锁cv.wait(lock,[&]{return c=='A'&&flag||c=='B'&&!flag;}); // 等待条件 flag 为 true[]{return flag||!flag;}cout<<"我是线程"<<c<<endl;flag = !flag;cv.notify_one(); // 通知另一个线程sleep(1);}
}int main()
{//创建AB线程std::thread threadA(Print,'A');std::thread threadB(Print,'B');// 等待线程结束threadA.join();threadB.join();return 0;
}
版本2(使用phtread.h库)
#include<iostream>
#include<pthread.h>
#include<unistd.h>
using namespace std;//声明互斥锁和条件变量
pthread_mutex_t mtx = PTHREAD_MUTEX_INITIALIZER;
pthread_cond_t cv = PTHREAD_COND_INITIALIZER;bool flag = false;
void* Print(void* args)
{char c = *(static_cast<char*>(args));for(int i = 0;i<5;i++){//先上锁为敬pthread_mutex_lock(&mtx);//判断条件变量while(c=='A'&&flag||c=='B'&&!flag){pthread_cond_wait(&cv,&mtx);//线程阻塞,等待唤醒}cout<<"我是线程"<<c<<endl;flag = !flag;sleep(1);pthread_cond_signal(&cv);//唤醒其它线程pthread_mutex_unlock(&mtx);//解锁,当其它线程被唤醒时能拿到锁}return nullptr;
}
int main()
{//1.线程声明pthread_t threadA,threadB;char c1 = 'A',c2 = 'B';//2.创建线程pthread_create(&threadA,nullptr,Print,(void*)&c1);pthread_create(&threadB,nullptr,Print,(void*)&c2);//3.线程等待回收pthread_join(threadA,nullptr);pthread_join(threadB,nullptr);//4.互斥锁和条件变量销毁pthread_mutex_destroy(&mtx);pthread_cond_destroy(&cv);return 0;
}
这里有几个需要注意的要点:
1.pthread_create
中的线程函数返回值必须是void类型的,参数为void,也就是指针类型
2.使用 while 循环则可以避免虚假唤醒的问题。当线程被唤醒时,它会重新检查条件是否满足。如果条件不满足,线程将继续等待,直到条件满足为止。这样可以确保线程在接收到正确的信号时才会继续执行。
👉🏻按序打印
原题链接:按序打印
mycode:
class Foo {
mutex mtx;
condition_variable cv;
int num = 0;
public:Foo() {}void first(function<void()> printFirst) {// printFirst() outputs "first". Do not change or remove this line.unique_lock<mutex> lock(mtx);cv.wait(lock,[&]{return num==0;});printFirst();num = (num+1)%3;cv.notify_all();//唤醒全部线程}void second(function<void()> printSecond) {// printSecond() outputs "second". Do not change or remove this line.//上锁unique_lock<mutex> lock(mtx);cv.wait(lock,[&]{return num==1;});printSecond();num = (num+1)%3;cv.notify_one();//唤醒一个线程}void third(function<void()> printThird) {// printThird() outputs "third". Do not change or remove this line.unique_lock<mutex> lock(mtx);cv.wait(lock,[&]{return num==2;});printThird();num = (num+1)%3;//cv.notify_one();//唤醒一个线程}
};
这里要注意的是唤醒线程只需要一个notify_all和notify_one就行了,这样就只会执行3次操作而已,不会过多导致时间超时
为什么呢?因为不管哪个函数先进入,如果因为条件不满足,会堵塞在那里,等待被唤醒,一个notify_all先将其余两个线程唤醒,此时这两个线程过来查看条件变量情况,一个会成功,一个会失败阻塞,这个时候还差一个线程还没执行完全在等待,就再来一个notify_one唤醒即可。
👉🏻 H2O 生成
原题链接:H2O 生成
mycode:
int num_h = 0;
class H2O {
mutex mtx;
condition_variable cv;
public:H2O() {}void hydrogen(function<void()> releaseHydrogen) {// releaseHydrogen() outputs "H". Do not change or remove this line.unique_lock<mutex> lock(mtx);cv.wait(lock,[&]{return num_h<2;});//H的数量要小于2releaseHydrogen();++num_h;cv.notify_all();//唤醒氧线程}void oxygen(function<void()> releaseOxygen) {// releaseOxygen() outputs "O". Do not change or remove this line.unique_lock<mutex> lock(mtx);cv.wait(lock,[&]{return num_h==2;});num_h = 0;//H与O匹配消耗完后清空releaseOxygen();cv.notify_all();//唤醒氢气线程}
};
如上便是本期的所有内容了,如果喜欢并觉得有帮助的话,希望可以博个点赞+收藏+关注🌹🌹🌹❤️ 🧡 💛,学海无涯苦作舟,愿与君一起共勉成长