• 线程异步通信


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

    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()时阻塞等待结果。

  • 相关阅读:
    在CMD下用java命令出现“找不到或无法加载主类”问题
    去除后台ckeditor的style="...."的样式
    php图片上传
    html图片预览
    论文阅读
    论文阅读
    论文阅读
    论文阅读
    论文阅读
    论文阅读
  • 原文地址:https://www.cnblogs.com/y4247464/p/16115185.html
Copyright © 2020-2023  润新知