赞
踩
和同学闲聊,谈到多线程中的经典问题——生产者-消费者问题:要求实现两个线程,一个线程负责对全局变量进行+1操作,一个线程负责打印更新后的值。自己从事code多年,自以为对多线程了解深入,不假思索,写出了下面的代码:
- #include <iostream>
- #include <mutex>
- #include <thread>
- #include <iostream>
- #include <condition_variable>
-
- static volatile bool g_flag = true; //线程运行标志
- static volatile int g_value = 0; //打印变量
- static std::condition_variable g_cond; //条件变量
- static std::mutex g_mutex; //互斥锁
-
- //打印线程入口函数
- void print_thread(void)
- {
- printf("print thread enter \n");
- while (g_flag) {
- std::unique_lock<std::mutex> lk(g_mutex);
- auto ret = g_cond.wait_for(lk, std::chrono::milliseconds(5000));
- if (ret == std::cv_status::no_timeout) {
- std::cout << "pppppppppp print thread " << g_value << std::endl;
- } else {
- printf("print thread wait timeout\n");
- }
- }
- printf("print thread leave \n");
- }
-
- //计算线程入口函数
- void add_thread(void)
- {
- printf("add thread enter \n");
- while (g_value < 10) {
- {
- std::unique_lock<std::mutex> lk(g_mutex);
- g_value++;
- std::cout << "++++++++++ add thread " << g_value << std::endl;
- }
- g_cond.notify_one();
- }
- printf("add thread leave \n");
- }
-
- int main(int argc, char **argv)
- {
- std::thread thread_print(print_thread);
- std::thread thread_add(add_thread);
-
- getchar();
-
- g_flag = false;
-
- if (thread_print.joinable())
- thread_print.join();
- if (thread_add.joinable())
- thread_add.join();
-
- return 0;
- }

运行之后,结果令我很尴尬:
分析原因,试着在分析线程调用g_cond.notify_one()之后添加等待,代码如下:
- void add_thread(void)
- {
- printf("add thread enter \n");
- while (g_value < 10) {
- {
- std::unique_lock<std::mutex> lk(g_mutex);
- g_value++;
- std::cout << "++++++++++ add thread " << g_value << std::endl;
- }
- g_cond.notify_one();
- std::this_thread::sleep_for(std::chrono::seconds(1));
- }
- printf("add thread leave \n");
- }
就这样,问题解决了:
由此可知,是由于计算线程运行太快,而打印线程运行慢,造成condition_variable信号丢失导致。很明显,要解决这样的问题,在计算线程增加等待不是一个好的解决方案,因为等待时间不好控制:时间太短,打印线程没有处理完,仍会导致condition_variable信号丢失;时间太长,导致打印线程等待,造成时间浪费。新的方案,使用两个condition_variable,一个用于等待打印,一个用于等待计算,代码如下:
- #include <iostream>
- #include <mutex>
- #include <thread>
- #include <iostream>
- #include <condition_variable>
-
- volatile bool g_flag = true; //运行标志
- volatile int g_value = 0; //变量
- volatile bool g_print_able = false; //是否可以打印
- std::condition_variable g_cond_add_enable; //计算条件变量
- std::condition_variable g_cond_print_enable; //打印条件变量
- std::mutex g_mutex;
-
- //打印线程入口函数
- void print_thread(void)
- {
- printf("print thread enter \n");
- while (g_flag) {
- {
- std::unique_lock<std::mutex> lk(g_mutex);
- g_cond_print_enable.wait(lk, [&] {return g_print_able; }); //等待打印
- std::cout << "pppppppppp print thread " << g_value << std::endl;
- g_print_able = false;
- }
- g_cond_add_enable.notify_one(); //通知计算
-
- }
- printf("print thread leave \n");
- }
-
- //计算线程入口函数
- void add_thread(void)
- {
- printf("add thread enter \n");
- while (g_value < 10) {
- if (g_value != 0) {
- std::unique_lock<std::mutex> lk(g_mutex);
- g_cond_add_enable.wait(lk, [] {return !g_print_able; }); //等待计算
- g_value++;
- std::cout << "++++++++++ add thread " << g_value << std::endl;
- g_print_able = true;
- } else { //第一次计算,不等待,直接计算,否则造成死锁
- g_value++;
- g_print_able = true;
- std::cout << "++++++++++ add thread " << g_value << std::endl;
- }
- g_cond_print_enable.notify_one(); //通知打印
- }
- printf("add thread leave \n");
- }
-
- int main(int argc, char **argv)
- {
- std::thread thread_print(print_thread);
- std::thread thread_add(add_thread);
-
- getchar();
-
- g_flag = false;
-
- if (thread_print.joinable())
- thread_print.join();
- if (thread_add.joinable())
- thread_add.join();
-
- return 0;
- }

打印结果如下:
当然,这种方案仅适用于生产者,消费者性能处理相当的情况。如果生产者,消费者处理性能相差较大,该种方案并不合适。
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。