linux开发

任务唤醒

任务唤醒的封面

std::ccondition_variable

核心使用思路

std::condition_variable(条件变量)的核心作用是让一个/多个线程等待某个条件满足,当条件满足时,由其他线程唤醒等待的线程。 关键组合:std::condition_variable + std::mutex + 共享条件(布尔变量/计数器等)。 核心唤醒函数:

  • notify_one():唤醒一个等待的线程(随机/按调度策略)。
  • notify_all():唤醒所有等待的线程。

完整可运行示例

下面通过一个“生产者-消费者”示例,展示如何唤醒等待的任务:

#include <iostream>
#include <thread>
#include <mutex>
#include <condition_variable>
#include <queue>

// 全局共享资源(模拟任务队列)
std::queue<int> task_queue;
// 互斥锁:保护共享资源和条件判断
std::mutex mtx;
// 条件变量:用于线程间同步
std::condition_variable cv;
// 停止标志:控制线程退出
bool stop_flag = false;

// 消费者线程(等待任务并执行)
void consumer() {
    while (true) {
        // 必须用unique_lock(条件变量需要解锁/加锁的灵活性)
        std::unique_lock<std::mutex> lock(mtx);
        
        // 等待条件满足:队列非空 或 停止标志为true
        // 注意:必须用while循环(防止虚假唤醒)
        cv.wait(lock, []() { 
            return !task_queue.empty() || stop_flag; 
        });

        // 检查是否需要退出
        if (stop_flag && task_queue.empty()) {
            std::cout << "消费者线程退出" << std::endl;
            break;
        }

        // 执行任务(取出队列元素)
        int task = task_queue.front();
        task_queue.pop();
        lock.unlock(); // 解锁,让生产者可以继续生产(非必须,但能提升性能)

        std::cout << "处理任务:" << task << std::endl;
        std::this_thread::sleep_for(std::chrono::milliseconds(100)); // 模拟任务耗时
    }
}

// 生产者线程(生产任务并唤醒消费者)
void producer() {
    for (int i = 1; i <= 5; ++i) {
        {
            std::lock_guard<std::mutex> lock(mtx);
            task_queue.push(i);
            std::cout << "生产任务:" << i << std::endl;
        } // 手动解锁,避免唤醒时还持有锁

        // 唤醒一个等待的消费者线程
        cv.notify_one();
        std::this_thread::sleep_for(std::chrono::milliseconds(50));
    }

    // 生产完毕,设置停止标志并唤醒所有消费者
    {
        std::lock_guard<std::mutex> lock(mtx);
        stop_flag = true;
    }
    cv.notify_all();
}

int main() {
    std::thread consumer_thread(consumer);
    std::thread producer_thread(producer);

    consumer_thread.join();
    producer_thread.join();

    return 0;
}

关键代码解释

  1. 等待逻辑(消费者):

    cv.wait(lock, []() { return !task_queue.empty() || stop_flag; });
    
    • wait 会先解锁 mutex,让线程进入等待状态,释放CPU资源。
    • 第二个参数是条件判断谓词:只有当谓词返回 true 时,线程才会被唤醒并重新加锁;否则会继续等待(避免“虚假唤醒”)。
    • 若省略谓词,需手动用 while 检查条件:
      while (task_queue.empty() && !stop_flag) {
          cv.wait(lock);
      }
      
  2. 唤醒逻辑(生产者):

    • cv.notify_one():唤醒任意一个等待在该条件变量上的线程(适合单消费者/多消费者但只需一个处理的场景)。
    • cv.notify_all():唤醒所有等待的线程(适合多消费者、需要全部处理收尾的场景)。
  3. 锁的使用:

    • 操作共享资源(task_queue/stop_flag)时必须加锁(std::lock_guard/std::unique_lock)。
    • wait 要求传入 std::unique_lock(因为需要在等待时临时解锁),而 lock_guard 是不可解锁的,无法用于 wait。

运行前置条件

  • 编译器支持 C++11 及以上(std::condition_variable 是 C++11 引入的)。
  • 编译命令(以g++为例):
    g++ -std=c++11 -pthread condition_variable_demo.cpp -o demo
    ./demo
    
    注意:必须加 -pthread 链接线程库,否则会报链接错误。

总结

  1. std::condition_variable 唤醒任务的核心是 notify_one()(唤醒一个)和 notify_all()(唤醒所有),需搭配 std::mutex 和条件判断使用。
  2. 等待时必须用 wait + 条件谓词(或 while 循环),避免虚假唤醒导致逻辑错误。
  3. 操作共享条件/资源时必须加锁,唤醒前建议解锁(提升性能),确保等待线程能立即获取锁。

问题1:wait() 和 wait_for() 的区别?

代码中的条件变量使用

int MpFileManager::workerThread()
{
    std::unique_lock<std::mutex> lock(mutex);
    is_run.store(true);
    while (is_run.load()) {
        queueCondition_.wait_for(lock, std::chrono::seconds(2), [this] {
            return !is_run.load() || work_start.load(); });
        if (!is_run.load())
            break;

        lock.lock();
    }
}

wait 和 wait_for 的区别

1. 等待方式

  • wait: 无限期等待,直到条件变量被通知或被虚假唤醒
  • wait_for: 有时间限制的等待,在指定时间段内等待,超时后自动返回

2. 超时控制

  • wait: 无超时参数,可能永久阻塞
  • wait_for: 接收时间间隔参数(如代码中的 std::chrono::seconds(2)),避免无限等待

3. 实际应用

  • wait: 适用于必须等待到条件满足的场景
  • wait_for: 适用于需要定期检查或避免死锁的场景,代码中每秒检查一次条件

4. 返回时机

  • wait: 只在条件满足或被通知时返回
  • wait_for: 可能因超时、条件满足或被通知而返回