c++多线程并发编程实战(二)

等待事件或条件

有时候我们想要让线程运行到某处停下,等待另一个线程通知后在继续运行。代码中创建了一个condition_variable,线程t2运行到17行处调用wait进行等待,wait(std::unique_lock, Predicate)需要传入一个或两个参数,第一个为std::unique_lock(为什么不能是std::lock_guard?),第二个为条件判断。wait()在内部调用传入的lambda函数,判断条件是否成立:若成立(lambda函数返回true),则wait()返回;否则(lambda函数返回false或者未传入),wait()解锁互斥,并令线程进入阻塞状态或等待状态。线程1将数据准备好后,即调用notify_one()或者notify_all()通知条件变量,线程2随之从休眠中觉醒(阻塞解除),重新在互斥上获取锁,再次查验条件:若条件成立,则从wait()函数返回,而互斥仍被锁住;若条件不成立,则线程甲解锁互斥,并继续等待。(防止虚假唤醒的情况)。

我们舍弃std::lock_guard而采用std::unique_lock,原因就在这里:线程2在等待期间,必须解锁互斥,而结束等待之后,必须重新加锁,但std::lock_guard无法提供这种灵活性。 假设线程2在休眠的时候,互斥依然被锁住,那么即使线程1备妥了数据,也不能锁住互斥。结果线程2所等待的条件永远不能成立,它将无止境地等下去。17行[]{return true;}在实际业务中需要替换为实际业务的判断条件。

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

int main() {
    std::mutex mutex1;
    std::condition_variable conditionVariable;
    std::thread t1([&]{
        std::lock_guard<std::mutex> guard(mutex1);
        std::this_thread::sleep_for(std::chrono::milliseconds(1000));
        conditionVariable.notify_all();
        std::cout << "t1 finished \n" << std::endl;
    });
    std::thread t2([&]{
        std::unique_lock<std::mutex> uniqueLock(mutex1);
        conditionVariable.wait(uniqueLock, []{return true;});
        std::cout << "t2 finished" << std::endl;
    });
    t1.join();
    t2.join();
}

从线程获取返回值

我们可以让线程1把结果写入到一个共享的变量中,然后线程2在一个while循环中检查值是否就绪,或者通过一个定时程序定时检查返回值是否就绪,毫无疑问这样做效率很低,它一直在浪费CPU的时间片。我们可以通过上面通知的方法去取,更直接的,我们可以让线程返回一个值std::future<>,我们直接调用get方法就可以从某个线程中获取返回值,如果值尚未返回,则当前线程会阻塞等待。

#include <thread>
#include <iostream>
#include <future>

int func() {
    std::this_thread::sleep_for(std::chrono::seconds(2));
    return 1;
}

int main() {
    std::future<int> future = std::async(func);
    std::cout << "do other stuff" << std::endl;
    std::cout << future.get();
}
  • std::async()方法传入一个可调用对象,返回一个std::future<>,当前线程可以继续做其他事情,然后在future.get()处等待线程返回值。
  • td::packaged_task<>连结了future对象与函数(或可调用对象)。std::packaged_task<>对象在执行任务时,会调用关联的函数(或可调用对象),把返回值保存为future的内部数据,std::packaged_task<>的类型是函数签名,传入一个可调用对象,std::packaged_task<>本身也是可调用对象,直接调用就是直接异步线程执行,也可以显式地扔给线程执行。
#include <iostream>
#include <future>

int add(int a, int b)
{
    return a + b;
}

int main()
{
    // 创建一个打包任务
    std::packaged_task<int(int,int)> task(add);

    // 获得与任务关联的 future 对象
    std::future<int> result = task.get_future();

    // 调用 packaged_task,将参数传递给 add
    task(2, 3);

    // 输出结果
    std::cout << "Result is " << result.get() << std::endl;

    return 0;
}

显示交给线程执行:

#include <iostream>
#include <future>
#include <thread>
#include <chrono>

int add(int a, int b)
{
    std::this_thread::sleep_for(std::chrono::seconds(2));
    return a + b;
}

int main()
{
    // 创建一个打包任务
    std::packaged_task<int(int,int)> task(add);

    // 获得与任务关联的 future 对象
    std::future<int> result = task.get_future();

    // 启动一个新线程执行任务
    std::thread worker(std::move(task), 2, 3);

    // 输出结果
    std::cout << "Result is " << result.get() << std::endl;

    return 0;
}
  • 使用Promise,Promise更加灵活,适合多个线程协同工作。std::promise给出了一种异步求值的方法(类型为T),某个std::future对象与结果关联,能延后读出需要求取的值。在promise调用set_value()设置值之前,调用future.get()的线程将会阻塞等待。
#include <iostream>
#include <thread>
#include <future>

int main()
{
    // 创建 promise 对象
    std::promise<int> valuePromise;

    // 获取与 promise 对象关联的 future 对象
    std::future<int> valueFuture = valuePromise.get_future();

    // 启动一个新线程设置值
    std::thread setterThread([&valuePromise]() {
        // 模拟耗时计算
        std::this_thread::sleep_for(std::chrono::seconds(2));

        // 设置值
        valuePromise.set_value(42);
    });

    // 在主线程中等待并获取值
    std::cout << "Waiting for the value..." << std::endl;
    int value = valueFuture.get();
    std::cout << "Received value: " << value << std::endl;

    setterThread.join();

    return 0;
}
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值