如何优雅的退出线程(condition_variable )

本文介绍了如何在C++中使用条件变量实现线程安全的退出机制,特别是在析构函数中通知等待的线程并确保线程退出。作者展示了如何在Logger类中使用std::thread,std::mutex和std::condition_variable来处理日志消息并发生产。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

C++多线程并发编程入门(目录)

使用条件变量来实现在析构函数中再次向线程发出一次notify,之后join 线程,等等线程安全退出。

#pragma once
#include <iostream>
#include <fstream>
#include <string>
#include <string_view>
#include <mutex>
#include <queue>
#include <sstream>

using namespace std;
class Logger
{
public:
	Logger();
	virtual ~Logger();
	Logger(const Logger& src) = delete;
	Logger& operator=(const Logger& rhs) = delete;
	void log(std::string entry);
private:
	void processLogging();
	std::mutex m_mutex;
	std::condition_variable m_condition_var;
	std::queue<std::string> m_queue;
	std::thread m_thread;
	bool m_have_to_exit = false;
};
Logger::Logger()
{
	m_thread = thread{ &Logger::processLogging, this };
	cout << "Logger::Logger()" << endl;
}

void Logger::log(std::string entry)
{
	unique_lock<std::mutex> lock(m_mutex);
	m_queue.push(entry);
	m_condition_var.notify_all();
}

Logger::~Logger()
{
	cout << "Logger::~Logger()" << endl;
	{
		unique_lock<std::mutex> lock(m_mutex);
		m_have_to_exit = true;
		m_condition_var.notify_all();
	}
	m_thread.join();//这一句不能被lock控制,否则退不出去
}

void Logger::processLogging()
{
	cout << "Logger::processLogging() begin run...." << endl;
	ofstream logFile("log.txt");

	if (logFile.fail())
	{
		cerr << "Failed to open logfile." << endl;
		return;
	}

	unique_lock<std::mutex> lock(m_mutex);
	while (true)
	{
		if (!m_have_to_exit)
		{
			m_condition_var.wait(lock); 
		}
		lock.unlock();
		while (true)//循环处理队列直到为空
		{
			lock.lock();
			if (m_queue.empty())
			{
				break;
			}
			else
			{
				logFile << m_queue.front() << endl;
				m_queue.pop();
			}
			lock.unlock();
		}
		if (m_have_to_exit)
		{
			cout << "m_have_to_exit is true.\n";
			break;
		}
	}
	cout << "Logger::processLogging() exited." << endl;
}

void logSomeMessage(int id, Logger& logger)
{
	for (int i = 0; i < 5; ++i)
	{
		stringstream ss;
		ss << " Thread " << id<< " log message the " << i <<"th times.";
		logger.log(ss.str());
	}
}

int main()
{
	cout << "hello main.\n";
	Logger logger;
	cout << "ready to create producer" << endl;
	vector<thread> threads;
	for (int i = 0; i < 5; ++i)
	{
		cout << "times: " << i << "\n";
		threads.emplace_back(logSomeMessage, i, ref(logger));
	}
	for (auto& t : threads)
	{
		t.join();
	}
	cout << "main exited.\n";
	return 0;
}

程序输出:

文件输出:

 Thread 1 log message the 0th times.
 Thread 0 log message the 0th times.
 Thread 1 log message the 1th times.
 Thread 2 log message the 0th times.
 Thread 0 log message the 1th times.
 Thread 1 log message the 2th times.
 Thread 2 log message the 1th times.
 Thread 0 log message the 2th times.
 Thread 1 log message the 3th times.
 Thread 2 log message the 2th times.
 Thread 0 log message the 3th times.
 Thread 1 log message the 4th times.
 Thread 2 log message the 3th times.
 Thread 0 log message the 4th times.
 Thread 2 log message the 4th times.
 Thread 4 log message the 0th times.
 Thread 3 log message the 0th times.
 Thread 3 log message the 1th times.
 Thread 4 log message the 1th times.
 Thread 3 log message the 2th times.
 Thread 4 log message the 2th times.
 Thread 3 log message the 3th times.
 Thread 4 log message the 3th times.
 Thread 3 log message the 4th times.
 Thread 4 log message the 4th times.

参考:如何优雅地退出线程?_std::thread exit_帮主写书的博客-优快云博客

<think>好的,用户想了解C++中的condition_variable,并且希望有一个例子。我需要先回忆一下condition_variable的基本概念和用法,确保自己理解正确。 首先,condition_variableC++11引入的,用于线程间的同步,通常和互斥锁mutex一起使用。它允许线程在某个条件不满足时进入等待状态,直到其他线程通知它条件可能已经改变。这样能有效减少不必要的忙等待,提升效率。 用户需要举例,最好是一个典型的生产者-消费者模型。这个例子能清楚展示如何使用condition_variable、mutex以及unique_lock。我需要确保例子简洁,同时涵盖必要的步骤,比如等待条件、通知等。 接下来,要确保代码的正确性。比如,生产者生产数据后,通过notify_one唤醒一个消费者线程。消费者在等待时需要使用lambda表达式判断条件是否满足,防止虚假唤醒。同时,必须使用unique_lock,因为condition_variable的wait函数需要可解锁和重新加锁的功能。 另外,需要解释代码的各个部分,让用户明白每个组件的作用。比如,互斥锁保护共享资源,条件变量协调线程的执行顺序,以及如何避免死锁。 可能用户在使用时容易出错的地方,比如忘记在等待条件时使用循环检查,或者没有正确使用锁,导致数据竞争。这些需要在解释中强调,帮助用户避免常见错误。 最后,检查代码是否符合C++标准,比如正确的头文件、命名空间的使用,以及线程和锁的管理。确保例子能够编译运行,并且逻辑正确,没有竞争条件。</think>下面我将通过一个生产者-消费者模型的示例,分步骤说明C++中`condition_variable`的使用方法: ```cpp #include <iostream> #include <thread> #include <mutex> #include <condition_variable> #include <queue> std::mutex mtx; std::condition_variable cv; std::queue<int> data_queue; // 生产者线程函数 void producer(int count) { for (int i = 0; i < count; ++i) { { std::unique_lock<std::mutex> lock(mtx); data_queue.push(i); std::cout << "生产数据: " << i << std::endl; } // 自动解锁 cv.notify_one(); // 通知一个等待线程 std::this_thread::sleep_for(std::chrono::milliseconds(100)); } } // 消费者线程函数 void consumer() { while (true) { std::unique_lock<std::mutex> lock(mtx); cv.wait(lock, []{ return !data_queue.empty(); // 防止虚假唤醒 }); int data = data_queue.front(); data_queue.pop(); std::cout << "消费数据: " << data << std::endl; lock.unlock(); if (data >= 4) break; // 简单退出条件 } } int main() { std::thread t1(producer, 5); // 生产5个数据 std::thread t2(consumer); t1.join(); t2.join(); return 0; } ``` 关键要素解释: 1. **同步组件** - `mutex`:保护共享队列的互斥锁 - `condition_variable`:协调生产消费节奏 2. **核心操作** - `cv.wait()`:消费者等待时自动释放锁 - `cv.notify_one()`:生产数据后唤醒消费者 3. **必要特征** - 使用`unique_lock`而非`lock_guard`(需要可解锁特性) - 等待条件必须使用`while`或带条件的lambda(防御虚假唤醒) - 锁的作用域控制(通过`{}`控制锁范围) 4. **执行流程** 1. 生产者获取锁,放入数据 2. 解锁后通知消费者 3. 消费者被唤醒,检查队列是否非空 4. 获取数据后释放锁 注意事项: 1. 必须通过互斥锁保护共享资源 2. `notify_all()`可用于唤醒所有等待线程 3. 条件变量的等待操作会自动释放和重新获取锁 4. 销毁条件变量前应确保所有等待线程已停止 此模式广泛应用于:线程池任务调度、事件驱动系统、流水线处理等需要线程协调的场景。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

C++程序员Carea

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值