C++之future

发布时间 2023-07-08 11:16:54作者: 冰山奇迹

背景

在C++多线程编程中,同步线程间的操作和结果通常是一个关键问题。C++11引入了std::future这一同步原语,用于表示异步操作的结果。本文将介绍C++中std::future的使用方法、优势以及与其他同步方法的对比。

使用std::future

std::future表示一个异步操作的结果,可以用于获取操作的返回值或者等待操作完成。std::future通常与std::async、std::packaged_task或std::promise一起使用。

使用std::async

std::async是一个简单的异步操作启动器,它接受一个函数及其参数,并在一个新线程中执行该函数。std::async返回一个std::future对象,用于表示异步操作的结果。

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

int slow_operation() {
    std::this_thread::sleep_for(std::chrono::seconds(2));
    return 42;
}

int main() {
    std::future<int> result = std::async(slow_operation);
    std::cout << "Waiting for the result..." << std::endl;
    int value = result.get();
    std::cout << "Result: " << value << std::endl;
    return 0;
}

在这个示例中,我们使用std::async启动一个慢速操作slow_operation。std::async返回一个std::future对象,我们可以使用get()方法等待操作完成并获取结果。

使用std::packaged_task

std::packaged_task是一个通用的异步操作包装器,它接受一个函数并将其包装成一个任务。我们可以将任务传递给一个线程执行,然后使用std::future来表示任务的结果。

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

int slow_operation() {
    std::this_thread::sleep_for(std::chrono::seconds(2));
    return 42;
}

int main() {
    std::packaged_task<int()> task(slow_operation);
    std::future<int> result = task.get_future();
    std::thread t(std::move(task));
    std::cout << "Waiting for the result..." << std::endl;
    int value = result.get();
    std::cout << "Result: " << value << std::endl;
    t.join();
    return 0;
}

在这个示例中,我们使用std::packaged_task包装一个慢速操作slow_operation,然后将任务传递给一个线程执行。我们使用get_future()方法获取一个std::future对象,用于表示任务的结果。

使用std::promise

std::promise是一个低级别的异步操作同步原语,它允许我们在一个线程中设置操作的结果,并在另一个线程中等待该结果。std::promise与std::future一起使用,用于表示操作的结果。

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

void slow_operation(std::promise<int> result_promise) {
    std::this_thread::sleep_for(std::chrono::seconds(2));
    result_promise.set_value(42);
}

int main() {
    std::promise<int> result_promise;
    std::future<int> result = result_promise.get_future();
    std::thread t(slow_operation, std::move(result_promise));
    std::cout << "Waiting for the result..." << std::endl;
    int value = result.get();
    std::cout << "Result: " << value << std::endl;
    t.join();
    return 0;
}

在这个示例中,我们使用std::promise在slow_operation函数中设置操作的结果。我们使用get_future()方法获取一个std::future对象,用于表示操作的结果。

std::future的优势

相较于其他同步方法,如互斥锁(std::mutex)和条件变量(std::condition_variable),std::future具有以下优势:

  • 简化编程模型:std::future简化了线程间结果传递的编程模型,使得我们可以更容易地编写正确的并发代码。
  • 自动同步:std::future在获取结果时自动实现同步,无需显式地使用互斥锁或条件变量。
  • 异常安全:std::future可以在异步操作中捕获异常,并在获取结果时重新抛出,从而确保异常安全。

使用场景与比较

std::future适用于以下场景:

  • 需要在线程间传递结果的异步操作。

  • 需要等待异步操作完成的场景。

  • 需要捕获异步操作中的异常。 相较于其他同步方法,std::future具有更简洁的编程模型和更好的异常安全性。然而,在某些场景下,互斥锁和条件变量可能更适合,例如:

  • 需要保护复杂数据结构的访问。

  • 需要在线程间传递通知的场景(如生产者消费者队列)。

最后

总结一下,在这篇博客中,我们介绍了C++并发操作同步中的std::future,包括使用std::async、std::packaged_task和std::promise进行异步操作,以及std::future的优势和使用场景。std::future是一种强大的同步原语,可以帮助我们简化多线程编程中的同步问题。在实际应用中,我们需要根据具体的需求和场景选择合适的同步方法,以实现高效、稳定的多线程程序。