线程异步通信

在线程启动后,我们并不知道什么时候能获取到其返回的结果。在之前的处理中,会用条件变量将共享资源给锁住,让线程完成共享变量的处理后,来通知另外一个线程。

https://zhuanlan.zhihu.com/p/493225557

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

void testFuture(std::promise<std::string> p) {
    std::this_thread::sleep_for(std::chrono::seconds(2));
    std::cout << "begin set value" << std::endl;
    p.set_value("Test Future value");  //是在set_value就返回值,还是在整个线程函数退出后返回
    std::this_thread::sleep_for(std::chrono::seconds(2));
    std::cout << "end Test Future" << std::endl;
}

int main(int argc, char* argv[]) {
    //异步传输变量
    std::promise<std::string> p;
    //用来获取线程异步值
    auto future = p.get_future();

    //将p移动到线程函数中,它只允许使用一次,不允许拷贝
    auto th = std::thread(testFuture, std::move(p));
    std::cout << "begin future get" << std::endl;
    std::cout << "future get: " << future.get() << std::endl;
    std::cout << "end future get" << std::endl;

    th.join();

    return 0;
}

输出结果:

 

 程序启动后,有两个线程:
主线程打印“begin future get”,然后阻塞等待future.get()返回。
子线程中,先是打印"begin set value",提示要开始调用p.set_value()了,set_value()调用成功后,主线程的
get()方法才返回。也就是说使用promise,future异步获取线程中的结果,不需要等待处理结果的线程返回退出,只要调用了set,就可以立即get到。

 

packaged_task 异步调用函数打包

- packaged_task 包装函数为一个对象,用于异步调用。其返回值能通过std::future对象访问
- 与bind的区别,可异步调用,函数访问和获取返回值分开调用

packaged_task的使用场景目的很明确,就是适用于函数调用与获取函数返回值分离的场景。比如,一个接口在后台计算某个结果,该结果不会立即返回,我们也不需要等待它返回才能进行下一步操作。就可以使用packaged_task将函数包装一下,并通过get_future接口获得future对象,在我们需要获取结果时,通过future.get()即可。

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

std::string testPack(int index) {
    std::cout << "begin test pack" << index << std::endl;
    std::this_thread::sleep_for(std::chrono::seconds(2));
    return "testPack return";
}

int main() {
    std::packaged_task<std::string(int)> task(testPack);
    auto result = task.get_future();  //获取未来值对象,以便在未来获取接口返回值

    std::thread th(std::move(task), 110);  //在子线程中调用接口

    //测试是否超时 
    //在实际的业务代码中也要有超时判断,防止接口内部死锁,一直阻塞等待结果
    for (int i = 0; i < 30; i++) {
        if (result.wait_for(std::chrono::milliseconds(100)) != std::future_status::timeout)
            continue;
    }

    std::cout << "begin get result" << std::endl;
    std::cout << "result get " << result.get() << std::endl;  //阻塞等待返回结果

    th.join();
    getchar();
    return 0;
}

 

std::async

C++异步运行一个函数,并返回保有其结果的std::future
- launch::deferred 延迟执行,并且不创建线程,在调用wait和get时,调用函数代码
- launch::async 创建线程(默认)
- 返回的线程函数的返回值类型std::future<xxx> (xxx表示线程函数的返回值类型)
- re.get()阻塞等待获取结果

async与packaged_task的作用相似,都是包装一个函数调用,在未来获取该调用的返回值。不同的是async可以在内部选择创建线程,将thread封装了起来。
代码示例:

1. 不创建线程启动异步任务

string testAsync(int index) {
    cout << index<< " begin in testAsync, id:" << this_thread::get_id() << endl;
    this_thread::sleep_for(chrono::seconds(3));
    return "testAsync string return";
}

int main() {
    //创建异步线程
    //不创建线程启动异步任务
    cout << "main thread ID" << this_thread::get_id() << endl;
    auto future = async(launch::deferred, testAsync, 100);
    this_thread::sleep_for(chrono::seconds(1));
    cout << "begin future get" << endl;
    cout << "get:"<< future.get() <<endl;
    cout << "end future get" << endl;
    getchar();
    return 0;
}

 启动后:

//先打印
main thread ID788
//间隔一秒
begin future get
//间隔三秒
100 begin in testAsync, id:788
get:testAsync string return
end future get

说明async在不创建线程的情况下是和主线程同步的。并且只有在调用future.get()方法时,才会进入到被包装函数中执行。

2. 创建线程启动异步任务

string testAsync(int index) {
    cout << index<< " begin in testAsync, id:" << this_thread::get_id() << endl;
    this_thread::sleep_for(chrono::seconds(3));
    return "testAsync string return";
}

int main() {
    //创建线程启动异步任务
    cout << "=====创建异步线程====" << endl;
    auto future2 = async(testAsync, 101);
    this_thread::sleep_for(chrono::seconds(1));
    cout << "begin future2 get" << endl;
    cout << "get:" << future2.get() << endl;
    cout << "end future2 get" << endl;
    getchar();
    return 0;
}

打印结果

//执行后先打印以下两行
=====创建异步线程==== 101 begin in testAsync, id:11176
//间隔一秒
begin future2 get
//间隔两秒
get:testAsync string return end future2 get

以创建线程的方式执行异步任务,一旦将函数包装后,就会立即执行,并且在get()时阻塞等待结果。

posted @ 2022-04-09 08:25  朱果果  阅读(104)  评论(0编辑  收藏  举报