C++ wait/notify机制

本文详细解析C++中的wait/notify机制,包括notify_one和notify_all的区别,以及如何在threadA、threadB和threadC中实现线程间的有序唤醒。通过实例演示了条件变量和互斥锁的使用,帮助理解线程间的协作与控制。

C++ wait/notify机制

notify_one:唤醒等待线程中的一个,唤醒的线程顺序执行。
notify_all:唤醒所有等待的线程,唤醒的线程抢占式执行。
wait:等待。需要其它的接口来唤醒。

instance analysis

#include <iostream>
#include <thread>
#include <condition_variable>
#include <mutex>//demo
#include <unistd.h>

std::mutex mtx_syn;
std::condition_variable cv_syn;
std::condition_variable cv_syn_1;
bool ready = false;//线程A

void threadA(int id)
{
    while (1)
    {
        std::unique_lock<std::mutex> lck(mtx_syn);
        while (!ready)
        {
            cv_syn.wait(lck);
        }
        // ...
        std::cout << "threadA " << id << '\n';
        usleep(500*1000);
        cv_syn.notify_one();   // 唤醒等待线程中的一个
        cv_syn.wait(lck);      // 等待
    }

}// 线程B
void threadB(int id)
{
    while (1)
    {
        //新创建的 unique_lock 对象管理 Mutex 对象 m,并尝试调用 m.lock() 对 Mutex 对象进行上锁,如果此时另外某个 unique_lock 对象已经管理了该 Mutex 对象 m,则当前线程将会被阻塞
        std::unique_lock<std::mutex> lck(mtx_syn);
        while (!ready)
        {
            cv_syn.wait(lck);
        }
        // ...
        std::cout << "threadB " << id << '\n';
        usleep(500*1000);
	cv_syn.notify_one();// 唤醒等待线程中的一个
	cv_syn.wait(lck);
    }
}

// 线程C
void threadC(int id)
{
    while (1)
    {
        std::unique_lock<std::mutex> lck(mtx_syn);
        while (!ready) cv_syn.wait(lck);
        // ...
        std::cout << "threadC " << id << '\n';
        usleep(500*1000);
        cv_syn.notify_one();  // 唤醒等待线程中的一个线程
        cv_syn.wait(lck);
    }
}


void go()
{
    std::unique_lock<std::mutex> lck(mtx_syn);
    ready = true;
    //cv_syn.notify_one(); // 唤醒等待线程中的一个线程,notify_one()唤醒的线程顺序执行
    cv_syn.notify_all();  //唤醒的线程抢占式知性
}

int main()
{
    //线程同步
    std::thread threads[5];
    threads[0] = std::thread(threadA, 0);
    threads[1] = std::thread(threadB, 1);
    threads[2] = std::thread(threadC, 2);
    std::cout << "3 threads ready to race...\n";
    go();                       // go!

    for (auto& th : threads) th.join();
}

g++ main.cpp -o main -std=c++11 -lpthread

C++ 中,`std::future` 和 `std::promise` 是用于线程间通信的重要工具,它们属于 C++11 引入的并发特性的一部分。`std::promise` 用于设置一个值或异常,而 `std::future` 用于获取这个值或处理异常。 ### 基本用法 一个 `std::promise` 对象与其关联的 `std::future` 对象之间存在一对一的关系。`std::promise` 用于提供一个值,而 `std::future` 用于在将来某个时刻获取该值。如果值尚未准备好,`std::future` 的 `get()` 方法会阻塞,直到值可用。 #### 示例代码 下面是一个简单的使用 `std::promise` 和 `std::future` 的示例: ```cpp #include <iostream> #include <future> #include <thread> void setter(std::promise<int>&& p) { int result = 42; p.set_value(result); // 设置 promise 的值 } int main() { std::promise<int> p; std::future<int> f = p.get_future(); // 获取与 promise 关联的 future std::thread t(setter, std::move(p)); // 启动线程,并传递 promise std::cout << "等待结果..." << std::endl; int result = f.get(); // 阻塞,直到 promise 设置了值 std::cout << "结果为: " << result << std::endl; t.join(); // 等待线程结束 return 0; } ``` 在这个例子中,`setter` 函数通过 `std::promise` 设置了一个值 `42`,主线程通过 `std::future` 获取这个值。`f.get()` 会阻塞,直到 `p.set_value()` 被调用。 ### 异常处理 `std::promise` 也可以用来传递异常。如果 `std::promise` 被销毁而没有调用 `set_value()` 或 `set_exception()`,它会自动设置一个 `std::future_error` 异常。 #### 示例代码(传递异常) ```cpp #include <iostream> #include <future> #include <thread> #include <stdexcept> void faultyFunction(std::promise<void>&& p) { try { throw std::runtime_error("Something went wrong!"); } catch (...) { p.set_exception(std::current_exception()); // 捕获异常并传递给 promise } } int main() { std::promise<void> p; std::future<void> f = p.get_future(); std::thread t(faultyFunction, std::move(p)); try { f.get(); // 抛出异常 } catch (const std::exception& e) { std::cout << "捕获到异常: " << e.what() << std::endl; } t.join(); return 0; } ``` 在这个例子中,`faultyFunction` 抛出了一个异常,并通过 `std::promise` 传递给了主线程。主线程的 `f.get()` 会重新抛出这个异常。 ### 线程池中的使用场景 在多线程编程中,特别是在线程池中,`std::future` 和 `std::promise` 可以用于任务提交和结果返回。例如,线程池可以接收一个任务,并返回一个 `std::future`,以便调用者可以在任务完成后获取结果。 #### 示例代码(线程池中的使用) ```cpp #include <iostream> #include <future> #include <thread> #include <vector> #include <functional> #include <queue> #include <mutex> #include <condition_variable> class ThreadPool { public: ThreadPool(size_t numThreads) { for (size_t i = 0; i < numThreads; ++i) { workers.emplace_back([this] { while (true) { std::function<void()> task; { std::unique_lock<std::mutex> lock(this->queue_mutex); this->condition.wait(lock, [this] { return this->stop || !this->tasks.empty(); }); if (this->stop && this->tasks.empty()) return; task = std::move(tasks.front()); tasks.pop(); } task(); } }); } } template<typename T> auto enqueue(T task) -> std::future<decltype(task())> { auto wrapper = std::make_shared<std::packaged_task<decltype(task())()>>(std::move(task)); { std::unique_lock<std::mutex> lock(queue_mutex); tasks.emplace([wrapper]() { (*wrapper)(); }); } condition.notify_one(); return wrapper->get_future(); } ~ThreadPool() { { std::unique_lock<std::mutex> lock(queue_mutex); stop = true; } condition.notify_all(); for (std::thread& worker : workers) worker.join(); } private: std::vector<std::thread> workers; std::queue<std::function<void()>> tasks; std::mutex queue_mutex; std::condition_variable condition; bool stop = false; }; int main() { ThreadPool pool(4); auto result = pool.enqueue([]() -> int { std::this_thread::sleep_for(std::chrono::seconds(1)); return 42; }); std::cout << "等待结果..." << std::endl; std::cout << "结果为: " << result.get() << std::endl; return 0; } ``` 在这个例子中,`ThreadPool` 类使用 `std::packaged_task` 和 `std::future` 来管理任务的执行和结果返回。调用者可以通过 `result.get()` 获取任务的结果。 ### 总结 `std::future` 和 `std::promise` 是 C++ 中用于线程间通信的重要工具,它们可以帮助开发者实现异步任务和结果返回。通过合理使用这些工具,可以提高程序的并发性和响应能力。[^2] ---
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值