【C++并发编程】(七)异步编程

(七)异步编程

异步编程(Asynchronous Programming是一种编程模型,它允许程序在不阻塞当前线程的情况下启动耗时操作(如I/O操作、网络请求等),并在这些操作完成时通知当前线程,反馈结果。这种模型使得程序在等待操作完成期间能够继续执行其他任务,从而提高了计算机资源的利用率。
异步编程可以看作是并发编程的一种策略或技术。 C++ 标准库中的std::future , std::asyncstd::packaged_taskstd::promise 用于支持异步编程。

std::future 是一个模板类,用于表示异步操作的结果。 std::future 对象通常不是直接创建的,而是与 std::asyncstd::packaged_taskstd::promise 配合使用。

std::async

std::async 是一个函数模板,它用于启动一个异步任务。这个函数接受一个可调用的对象(如函数、Lambda 表达式、函数对象等)作为参数,并在一个单独的线程上异步执行它。std::async 自动启动并管理异步任务,通过指定的执行策略(std::launch::async强制在单独的线程上异步执行函数;std::launch::deferred延迟执行,直到调用 std::future::get()std::future::wait() 时才执行函数)返回一个 std::future 对象。

下面展示如何使用 std::futurestd::async 来异步计算一个数的平方:

#include <iostream>  
#include <future>
#include <thread>
#include <chrono>
  
// 计算平方的函数  
int square(int x) {  
    auto start = std::chrono::system_clock::now();  
      
    // 为了模拟一个耗时的操作,我们让线程休眠一段时间  
    std::this_thread::sleep_for(std::chrono::seconds(2));  
      
    auto end = std::chrono::system_clock::now();  
    std::chrono::duration<double> diff = end - start;  
    std::cout << "Square computation took " << diff.count() << " seconds.\n";  
      
    return x * x;  
}  
  
int main() {  

    auto start_main = std::chrono::system_clock::now();  
      
    // 使用 std::async 启动一个异步任务,并传递 square 函数和参数 5  
    std::future<int> result = std::async(std::launch::async, square, 5);  
  

    std::cout << "Square computation is in progress...\n";  
  
    // 模拟主线程执行其他任务  
    std::this_thread::sleep_for(std::chrono::seconds(1));  
      

    auto after_other_task = std::chrono::system_clock::now();  
    std::chrono::duration<double> diff_other_task = after_other_task - start_main;  
    std::cout << "Main thread did something else after " << diff_other_task.count() << " seconds.\n";  
 
    // 调用 get()  获取square 函数的结果  
    // 注意:调用 get() 函数会阻塞当前线程,直到结果可用  
    int value = result.get();  
  
    // 输出结果和获取结果所用的时间  
    auto end_main = std::chrono::system_clock::now();  
    std::chrono::duration<double> diff_result = end_main - start_main;  
    std::cout << "The square of 5 is " << value << ". Got result after " << diff_result.count() << " seconds.\n";  
  
    return 0;  
}
Square computation is in progress...
Main thread did something else after 1.01517 seconds.
Square computation took 2.0062 seconds.
The square of 5 is 25. Got result after 2.00861 seconds.

在这个示例中,异步任务(计算5的平方)需要2秒钟来完成。然而,主线程在启动异步任务后立即继续执行,并在1秒钟后输出“Main thread is doing something else…”。这表明主线程在等待异步任务完成期间没有被阻塞,而是继续执行了其他任务。最后,当主线程需要异步任务的结果时,它调用result.get(),这时如果异步任务还没有完成,主线程将会被阻塞,直到结果可用。

std::packaged_task

std::packaged_task:一个类模板,用于将可调用对象(如函数、lambda 表达式或函数对象)包装成任务,然后可将其传递给线程异步地执行。

调用std::packaged_task::get_future()都会得到一个与之关联的std::future对象,这个对象可以通过.get()来获取任务完成后产生的结果。如果任务尚未完成,则获取结果的操作将阻塞调用线程,直到结果可用。

以下代码示例使用 std::packaged_task 实现与上小节中std::async相同的功能 :

#include <iostream>  
#include <future>  
#include <thread>  
#include <chrono>  
  
// 计算平方的函数  
int square(int x) {  
    auto start = std::chrono::system_clock::now();  
  
    // 为了模拟一个耗时的操作,我们让线程休眠一段时间  
    std::this_thread::sleep_for(std::chrono::seconds(2));  
  
    auto end = std::chrono::system_clock::now();  
    std::chrono::duration<double> diff = end - start;  
    std::cout << "Square computation took " << diff.count() << " seconds.\n";  
  
    return x * x;  
}  
  
int main() {  
    auto start_main = std::chrono::system_clock::now();  
  
    // 使用 std::packaged_task 包装 square 函数  
    std::packaged_task<int(int)> task(square);  
  
    // 从 packaged_task 获取 future 对象  
    std::future<int> result = task.get_future();  
  
    // 在另一个线程中运行 task  
    std::thread(std::move(task), 5).detach();  
  
    std::cout << "Square computation is in progress...\n";  
  
    // 模拟主线程执行其他任务  
    std::this_thread::sleep_for(std::chrono::seconds(1));  
  
    auto after_other_task = std::chrono::system_clock::now();  
    std::chrono::duration<double> diff_other_task = after_other_task - start_main;  
    std::cout << "Main thread did something else after " << diff_other_task.count() << " seconds.\n";  
  
    // 调用 get() 获取 square 函数的结果  
    // 注意:调用 get() 函数会阻塞当前线程,直到结果可用  
    int value = result.get();  
  
    // 输出结果和获取结果所用的时间  
    auto end_main = std::chrono::system_clock::now();  
    std::chrono::duration<double> diff_result = end_main - start_main;  
    std::cout << "The square of 5 is " << value << ". Got result after " << diff_result.count() << " seconds.\n";  
  
    return 0;  
}

相比于 std::asyncstd::packaged_task 将异步任务的创建和执行分离开来,提供了更高的自由度。使用 std::packaged_task,你可以显式地指定在哪个线程上执行任务,也可以更灵活地控制任务的生命周期。然而,这也意味着 std::packaged_task 的使用相对复杂一些,因为它需要更多的手动管理。

std::promise

std::promise 是一个类模板,它提供了在线程间传递值或异常的机制。通过这个机制,一个线程可以在任务执行的任意时刻使用 std::promise 对象来设置值或异常,而另一个线程则可以通过与 std::promise 对象相关联的 std::future 对象来检索这个值或异常。

示例

#include <iostream>  
#include <future>  
#include <thread>  
  
// 计算函数,接受一个promise对象和两个整数作为参数  
void calculate(std::promise<int> prom, int x, int y) {  
    try {  
        // 检查除数是否为0  
        if (y == 0) {  
            // 如果除数为0,则抛出运行时错误  
            throw std::runtime_error("Division by zero");  
        }  
        // 否则,将计算结果设置到promise对象中  
        prom.set_value(x / y); 
    } catch (...) {  
        // 捕获所有异常,并将当前异常设置到promise对象中  
        prom.set_exception(std::current_exception()); 
    }  
}  
  
int main() {  
    // 创建一个promise对象,用于存储异步操作的结果或异常  
    std::promise<int> prom;  
      
    // 从promise对象中获取一个future对象,用于获取异步操作的结果或异常  
    std::future<int> result = prom.get_future();  
      
    // 启动一个新线程来执行calculate函数,并将promise对象(通过移动语义传递)和两个整数作为参数  
    std::thread t(calculate, std::move(prom), 10,0);  
      
    // 调用result.get()来获取异步操作的结果,如果操作抛出异常,则get()会重新抛出该异常  
    try {  
        std::cout << "Result: " << result.get() << std::endl; // 如果操作成功,输出计算结果  
    } catch (const std::exception& e) {  
        std::cout << "Exception: " << e.what() << std::endl; // 如果操作抛出异常,输出异常信息  
    }  
      

    t.join();
      
    return 0;  
}
Result: Exception: Division by zero
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

二进制人工智能

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值