C++条件变量使用错误详解
1. 条件变量基础概念
1.1 什么是条件变量
条件变量是线程同步机制,允许线程等待特定条件成立,而不是忙等待。正确使用条件变量需要理解其与互斥锁的配合。
#include <iostream>
#include <thread>
#include <mutex>
#include <condition_variable>
#include <queue>
class BasicConditionVariable {
std::mutex mutex;
std::condition_variable cv;
bool data_ready = false;
int shared_data = 0;
public:
void producer() {
{
std::lock_guard<std::mutex> lock(mutex);
shared_data = 42;
data_ready = true;
}
cv.notify_one(); // 通知等待的消费者
}
void consumer() {
std::unique_lock<std::mutex> lock(mutex);
cv.wait(lock, [this] { return data_ready; }); // 等待条件成立
std::cout << "Consumed: " << shared_data << std::endl;
}
};
2. 常见的条件变量使用错误
2.1 丢失通知(Lost Wake-up)
class LostWakeupExample {
std::mutex mutex;
std::condition_variable cv;
bool condition = false;
public:
// 错误示例:通知在等待之前发生
void problematic_producer() {
std::this_thread::sleep_for(std::chrono::milliseconds(100)); // 模拟延迟
{
std::lock_guard<std::mutex> lock(mutex);
condition = true;
}
cv.notify_one(); // 可能发生在消费者等待之前!
}
void problematic_consumer() {
std::this_thread::sleep_for(std::chrono::milliseconds(200)); // 延迟开始
std::unique_lock<std::mutex> lock(mutex);
cv.wait(lock); // 永远等待,因为通知已经发生!
std::cout << "Consumer awakened" << std::endl;
}
// 正确做法:总是使用谓词
void correct_consumer() {
std::unique_lock<std::mutex> lock(mutex);
cv.wait(lock, [this] { return condition; }); // 使用谓词检查
std::cout << "Consumer correctly awakened" << std::endl;
}
};
2.2 虚假唤醒(Spurious Wake-up)
class SpuriousWakeupExample {
std::mutex mutex;
std::condition_variable cv;
std::queue<int> data_queue;
public:
// 错误示例:没有检查条件的wait
void wrong_consumer() {
std::unique_lock<std::mutex> lock(mutex);
cv.wait(lock); // 可能虚假唤醒!
// 队列可能仍然是空的!
if (!data_queue.empty()) { // 必须检查!
int value = data_queue.front();
data_queue.pop();
std::cout << "Processed: " << value << std::endl;
}
}
// 正确做法:使用谓词循环检查
void correct_consumer() {
std::unique_lock<std::mutex> lock(mutex);
// 使用lambda谓词,在唤醒后检查真实条件
cv.wait(lock, [this] {
return !data_queue.empty(); // 只有队列非空时才继续
});
int value = data_queue.front();
data_queue.pop();
std::cout << "Safely processed: " << value << std::endl;
}
void producer() {
{
std::lock_guard<std::mutex> lock(mutex);
data_queue.push(100);
}
cv.notify_one();
}
};
2.3 条件检查与锁的配合错误
class ConditionCheckError {
std::mutex mutex;
std::condition_variable cv;
int counter = 0;
const int MAX_COUNT = 5;
public:
// 错误示例:在锁外检查条件
void wrong_waiting() {
// 错误的顺序!
if (counter < MAX_COUNT) { // 在锁外检查 - 数据竞争!
std::unique_lock<std::mutex> lock(mutex);
cv.wait(lock); // 等待时条件可能已经改变!
}
}
// 正确做法:条件检查必须在锁的保护下
void correct_waiting() {
std::unique_lock<std::mutex> lock(mutex);
// 条件检查和wait必须在同一个锁的保护下
cv.wait(lock, [this] {
return counter < MAX_COUNT;
});
// 在这里,我们持有锁,并且条件为真
++counter;
}
void notifier() {
{
std::lock_guard<std::mutex> lock(mutex);
// 修改条件
}
cv.notify_all(); // 通知所有等待者重新检查条件
}
};
2.4 多个条件变量使用混乱
class MultipleConditionsError {
std::mutex mutex;
std::condition_variable cv_space_available;
std::condition_variable cv_data_available;
std::queue<int> buffer;
const size_t BUFFER_SIZE = 10;
bool shutdown = false;
public:
// 错误示例:错误的条件变量通知
void wrong_producer(int value) {
std::unique_lock<std::mutex> lock(mutex);
// 等待空间可用
while (buffer.size() >= BUFFER_SIZE) {
cv_space_available.wait(lock); // 等待空间
}
buffer.push(value);
// 错误:可能通知了错误的等待者
cv_space_available.notify_one(); // 应该通知数据可用!
}
void wrong_consumer() {
std::unique_lock<std::mutex> lock(mutex);
// 等待数据可用
while (buffer.empty()) {
cv_data_available.wait(lock); // 等待数据
}
int value = buffer.front();
buffer.pop();
// 错误:可能通知了错误的等待者
cv_data_available.notify_one(); // 应该通知空间可用!
}
// 正确做法:清晰的条件变量分工
void correct_producer(int value) {
std::unique_lock<std::mutex> lock(mutex);
cv_space_available.wait(lock, [this] {
return buffer.size() < BUFFER_SIZE || shutdown;
});
if (shutdown) return;
buffer.push(value);
cv_data_available.notify_one(); // 通知消费者有数据了
}
void correct_consumer() {
std::unique_lock<std::mutex> lock(mutex);
cv_data_available.wait(lock, [this] {
return !buffer.empty() || shutdown;
});
if (shutdown && buffer.empty()) return;
int value = buffer.front();
buffer.pop();
cv_space_available.notify_one(); // 通知生产者有空间了
}
void stop() {
{
std::lock_guard<std::mutex> lock(mutex);
shutdown = true;
}
cv_space_available.notify_all();
cv_data_available.notify_all();
}
};
2.5 等待超时处理错误
#include <chrono>
class TimeoutHandlingError {
std::mutex mutex;
std::condition_variable cv;
bool task_completed = false;
std::string result;
public:
// 错误示例:忽略超时返回值
void wrong_wait_with_timeout() {
std::unique_lock<std::mutex> lock(mutex);
// 忽略返回值!
cv.wait_for(lock, std::chrono::seconds(1));
// 可能因为超时而唤醒,但task_completed仍然是false!
if (task_completed) { // 必须检查!
std::cout << "Result: " << result << std::endl;
} else {
std::cout << "Timeout occurred" << std::endl;
}
}
// 正确做法:检查等待的返回值
void correct_wait_with_timeout() {
std::unique_lock<std::mutex> lock(mutex);
// 使用带谓词的wait_for,自动处理超时
bool success = cv.wait_for(lock, std::chrono::seconds(1),
[this] { return task_completed; });
if (success) {
std::cout << "Result: " << result << std::endl;
} else {
std::cout << "Timeout occurred" << std::endl;
// 处理超时逻辑
handle_timeout();
}
}
// 另一种正确做法:检查cv_status
void alternative_correct_wait() {
std::unique_lock<std::mutex> lock(mutex);
auto status = cv.wait_for(lock, std::chrono::seconds(1));
if (status == std::cv_status::no_timeout) {
// 被通知唤醒
if (task_completed) {
std::cout << "Result: " << result << std::endl;
}
} else {
// 超时
std::cout << "Timeout occurred" << std::endl;
handle_timeout();
}
}
void complete_task(const std::string& res) {
{
std::lock_guard<std::mutex> lock(mutex);
result = res;
task_completed = true;
}
cv.notify_one();
}
private:
void handle_timeout() {
std::cout << "Handling timeout situation" << std::endl;
}
};
3. 条件变量的正确使用模式
3.1 生产者-消费者模式
template<typename T>
class CorrectProducerConsumer {
std::mutex mutex;
std::condition_variable cv_producer;
std::condition_variable cv_consumer;
std::queue<T> queue;
size_t max_size;
bool shutdown = false;
public:
explicit CorrectProducerConsumer(size_t max_size = 10)
: max_size(max_size) {}
// 生产者:等待空间可用
bool produce(T item) {
std::unique_lock<std::mutex> lock(mutex);
// 等待条件:有空间或者需要关闭
cv_producer.wait(lock, [this] {
return queue.size() < max_size || shutdown;
});
if (shutdown) return false;
queue.push(std::move(item));
cv_consumer.notify_one(); // 通知消费者
return true;
}
// 消费者:等待数据可用
bool consume(T& item) {
std::unique_lock<std::mutex> lock(mutex);
// 等待条件:有数据或者需要关闭
cv_consumer.wait(lock, [this] {
return !queue.empty() || shutdown;
});
if (shutdown && queue.empty()) return false;
item = std::move(queue.front());
queue.pop();
cv_producer.notify_one(); // 通知生产者
return true;
}
void stop() {
{
std::lock_guard<std::mutex> lock(mutex);
shutdown = true;
}
cv_producer.notify_all();
cv_consumer.notify_all();
}
size_t size() const {
std::lock_guard<std::mutex> lock(mutex);
return queue.size();
}
};
3.2 屏障同步模式
class ThreadBarrier {
std::mutex mutex;
std::condition_variable cv;
const size_t thread_count;
size_t waiting_count = 0;
size_t generation = 0;
bool shutdown = false;
public:
explicit ThreadBarrier(size_t count) : thread_count(count) {}
void wait() {
std::unique_lock<std::mutex> lock(mutex);
if (shutdown) return;
size_t current_generation = generation;
++waiting_count;
if (waiting_count == thread_count) {
// 所有线程都到达,唤醒大家
++generation;
waiting_count = 0;
cv.notify_all();
} else {
// 等待其他线程
cv.wait(lock, [this, current_generation] {
return shutdown || generation > current_generation;
});
}
}
void stop() {
{
std::lock_guard<std::mutex> lock(mutex);
shutdown = true;
}
cv.notify_all();
}
};
3.3 事件通知模式
class EventNotifier {
std::mutex mutex;
std::condition_variable cv;
bool event_occurred = false;
std::string event_data;
bool shutdown = false;
public:
// 等待事件发生
bool wait_for_event(std::string& data,
std::chrono::milliseconds timeout = std::chrono::milliseconds::max()) {
std::unique_lock<std::mutex> lock(mutex);
bool success = cv.wait_for(lock, timeout, [this] {
return event_occurred || shutdown;
});
if (success && event_occurred) {
data = event_data;
event_occurred = false; // 重置事件,用于下次等待
return true;
}
return false;
}
// 通知事件发生
void notify_event(const std::string& data) {
{
std::lock_guard<std::mutex> lock(mutex);
event_data = data;
event_occurred = true;
}
cv.notify_one(); // 或 notify_all() 取决于需求
}
void stop() {
{
std::lock_guard<std::mutex> lock(mutex);
shutdown = true;
}
cv.notify_all();
}
};
4. 高级条件变量技巧
4.1 可中断的等待
class InterruptibleWait {
std::mutex mutex;
std::condition_variable cv;
bool condition = false;
std::exception_ptr interrupt_reason;
public:
void wait() {
std::unique_lock<std::mutex> lock(mutex);
cv.wait(lock, [this] {
if (interrupt_reason) {
std::rethrow_exception(interrupt_reason);
}
return condition;
});
// 条件满足,继续执行
}
void wait_with_timeout(std::chrono::milliseconds timeout) {
std::unique_lock<std::mutex> lock(mutex);
bool success = cv.wait_for(lock, timeout, [this] {
if (interrupt_reason) {
std::rethrow_exception(interrupt_reason);
}
return condition;
});
if (!success) {
throw std::runtime_error("Wait timeout");
}
}
void notify() {
{
std::lock_guard<std::mutex> lock(mutex);
condition = true;
}
cv.notify_all();
}
void interrupt(std::exception_ptr reason) {
{
std::lock_guard<std::mutex> lock(mutex);
interrupt_reason = reason;
}
cv.notify_all();
}
void interrupt(const std::string& message) {
interrupt(std::make_exception_ptr(std::runtime_error(message)));
}
void reset() {
std::lock_guard<std::mutex> lock(mutex);
condition = false;
interrupt_reason = nullptr;
}
};
4.2 条件变量与RAII模式结合
class RAIIConditionWaiter {
std::unique_lock<std::mutex> lock;
std::condition_variable& cv;
bool& condition;
public:
RAIIConditionWaiter(std::mutex& mutex, std::condition_variable& cv_ref, bool& cond)
: lock(mutex), cv(cv_ref), condition(cond)
{
// 在构造函数中等待条件
cv.wait(lock, [this] { return condition; });
}
// 移动构造
RAIIConditionWaiter(RAIIConditionWaiter&& other) noexcept
: lock(std::move(other.lock))
, cv(other.cv)
, condition(other.condition)
{}
// 禁止拷贝
RAIIConditionWaiter(const RAIIConditionWaiter&) = delete;
RAIIConditionWaiter& operator=(const RAIIConditionWaiter&) = delete;
~RAIIConditionWaiter() {
if (lock.owns_lock()) {
condition = false; // 重置条件
}
}
// 显式释放锁
void release() {
if (lock.owns_lock()) {
lock.unlock();
}
}
};
class ConditionWithRAII {
std::mutex mutex;
std::condition_variable cv;
bool resource_ready = false;
public:
void prepare_resource() {
{
std::lock_guard<std::mutex> lock(mutex);
resource_ready = true;
}
cv.notify_all();
}
void use_resource() {
// RAII方式等待资源就绪
RAIIConditionWaiter waiter(mutex, cv, resource_ready);
// 在这里,资源已经就绪,并且持有锁
std::cout << "Using resource safely" << std::endl;
// 析构函数会自动处理清理
}
};
5. 条件变量的测试和调试
5.1 单元测试模式
#include <gtest/gtest.h>
class ConditionVariableTest : public ::testing::Test {
protected:
template<typename ConditionSystem>
void test_notification(ConditionSystem& system) {
std::atomic<bool> consumer_started{false};
std::atomic<bool> consumer_finished{false};
std::string consumed_data;
// 消费者线程
std::thread consumer([&] {
consumer_started = true;
system.consume(consumed_data);
consumer_finished = true;
});
// 等待消费者开始等待
while (!consumer_started) {
std::this_thread::yield();
}
// 给消费者时间进入等待状态
std::this_thread::sleep_for(std::chrono::milliseconds(10));
// 生产者通知
system.produce("test_data");
// 等待消费者完成
consumer.join();
EXPECT_TRUE(consumer_finished);
EXPECT_EQ(consumed_data, "test_data");
}
template<typename ConditionSystem>
void test_timeout(ConditionSystem& system, std::chrono::milliseconds timeout) {
auto start = std::chrono::steady_clock::now();
std::string data;
bool success = system.consume_with_timeout(data, timeout);
auto end = std::chrono::steady_clock::now();
auto duration = std::chrono::duration_cast<std::chrono::milliseconds>(end - start);
EXPECT_FALSE(success); // 应该超时
EXPECT_GE(duration, timeout); // 至少等待了指定时间
EXPECT_LE(duration, timeout + std::chrono::milliseconds(50)); // 允许一些误差
}
};
class TestableProducerConsumer {
// 简单的可测试实现
public:
bool consume(std::string& data) { /* 实现 */ return true; }
void produce(const std::string& data) { /* 实现 */ }
bool consume_with_timeout(std::string& data, std::chrono::milliseconds timeout) {
/* 实现 */ return false;
}
};
TEST_F(ConditionVariableTest, BasicNotification) {
TestableProducerConsumer system;
test_notification(system);
}
TEST_F(ConditionVariableTest, TimeoutBehavior) {
TestableProducerConsumer system;
test_timeout(system, std::chrono::milliseconds(100));
}
5.2 死锁检测和调试
class DebuggableConditionVariable {
std::mutex mutex;
std::condition_variable cv;
bool condition = false;
// 调试信息
std::thread::id waiting_thread;
std::chrono::steady_clock::time_point wait_start;
public:
void wait(const std::string& debug_info = "") {
std::unique_lock<std::mutex> lock(mutex);
// 记录调试信息
waiting_thread = std::this_thread::get_id();
wait_start = std::chrono::steady_clock::now();
std::cout << "Thread " << std::this_thread::get_id()
<< " waiting for condition: " << debug_info << std::endl;
cv.wait(lock, [this] {
if (std::chrono::steady_clock::now() - wait_start > std::chrono::seconds(5)) {
std::cerr << "POTENTIAL DEADLOCK: Thread " << std::this_thread::get_id()
<< " waiting too long!" << std::endl;
// 这里可以添加更多调试信息或中断逻辑
}
return condition;
});
std::cout << "Thread " << std::this_thread::get_id()
<< " condition satisfied" << std::endl;
}
void notify() {
{
std::lock_guard<std::mutex> lock(mutex);
condition = true;
}
std::cout << "Notifying condition variable" << std::endl;
cv.notify_all();
}
void get_debug_info() const {
std::lock_guard<std::mutex> lock(mutex);
auto now = std::chrono::steady_clock::now();
auto wait_duration = now - wait_start;
std::cout << "Waiting thread: " << waiting_thread << std::endl;
std::cout << "Wait duration: "
<< std::chrono::duration_cast<std::chrono::seconds>(wait_duration).count()
<< " seconds" << std::endl;
std::cout << "Condition: " << (condition ? "true" : "false") << std::endl;
}
};
6. 最佳实践总结
6.1 条件变量使用黄金法则
class ConditionVariableBestPractices {
public:
static void demonstrate_best_practices() {
std::mutex mutex;
std::condition_variable cv;
bool condition = false;
int shared_data = 0;
// 法则1:总是使用谓词检查真实条件
auto consumer = [&] {
std::unique_lock<std::mutex> lock(mutex);
// GOOD: 使用谓词防止虚假唤醒和丢失通知
cv.wait(lock, [&] { return condition; });
std::cout << "Data: " << shared_data << std::endl;
};
// 法则2:修改条件时必须持有锁
auto producer = [&] {
{
// GOOD: 在锁的保护下修改条件
std::lock_guard<std::mutex> lock(mutex);
shared_data = 42;
condition = true;
}
// GOOD: 在锁外通知,提高性能
cv.notify_one();
};
// 法则3:处理多等待者的情况
auto multi_consumer = [&] {
std::unique_lock<std::mutex> lock(mutex);
// GOOD: 使用while循环或带谓词的wait处理多个消费者
while (!condition) {
cv.wait(lock);
}
// 处理数据...
};
// 法则4:正确关闭时的通知
auto shutdown_handler = [&] {
{
std::lock_guard<std::mutex> lock(mutex);
condition = true; // 或设置专门的shutdown标志
}
cv.notify_all(); // 通知所有等待者
};
}
};
6.2 常见错误检查清单
class ConditionVariableChecklist {
public:
static void review_usage() {
std::cout << "条件变量使用检查清单:" << std::endl;
std::cout << "1. 是否总是使用谓词来检查真实条件?" << std::endl;
std::cout << "2. 修改条件变量关联的条件时是否持有锁?" << std::endl;
std::cout << "3. 是否处理了虚假唤醒的可能性?" << std::endl;
std::cout << "4. 是否处理了丢失通知的情况?" << std::endl;
std::cout << "5. 通知操作是否在锁外进行?" << std::endl;
std::cout << "6. 是否使用了正确的通知函数(notify_one vs notify_all)?" << std::endl;
std::cout << "7. 超时等待是否检查了返回值?" << std::endl;
std::cout << "8. 多条件变量使用时是否分工明确?" << std::endl;
std::cout << "9. 关闭系统时是否通知了所有等待线程?" << std::endl;
std::cout << "10. 是否避免了在持有条件变量锁时执行耗时操作?" << std::endl;
}
};
通过遵循这些模式和最佳实践,可以避免条件变量的常见错误,构建出健壮、高效的并发C++应用程序。
C++条件变量常见错误解析
1万+

被折叠的 条评论
为什么被折叠?



