首页 > 编程笔记 > C++笔记 阅读:210

C++ std::async()和std::future:异步执行函数(附带实例)

线程可使我们在同一时间运行多个函数,这将帮助我们充分利用多处理器或多核系统的硬件。然而,线程要求显式、底层的操作。相比线程的另一选择是任务,在特定线程中运行一组工作。

C++ 标准没提供完备的任务库,通过 promise-future 通道,开发者可在不同的线程中异步执行函数并传递执行结果。在本节中,我们将看到如何使用 std::async() 和 std::future 实现这一点。

在本节的示例中,我们将使用如下函数:
void do_something()
{
    // simulate Long running operation
    {
        using namespace std::chrono_literals;
        std::this_thread::sleep_for(2s);
    }
    std::lock_guard<std::mutex> lock(g_mutex);
    std::cout << "operation 1 done" << '\n';
}

void do_something_else()
{
    // simulate Long running operation
    {
        using namespace std::chrono_literals;
        std::this_thread::sleep_for(1s);
    }
    std::lock_guard<std::mutex> lock(g_mutex);
    std::cout << "operation 2 done" << '\n';
}

int compute_something()
{
    // simulate long running operation
    {
        using namespace std::chrono_literals;
        std::this_thread::sleep_for(2s);
    }
    return 42;
}

int compute_something_else()
{
    // simulate Long running operation
    {
        using namespace std::chrono_literals;
        std::this_thread::sleep_for(1s);
    }
    return 24;
}
本节中,我们将使用 future,async() 和 future 都在 <future> 头文件的 std 命名空间中可用。

C++ std::async()和future的使用方式

在当前线程继续执行而不需要等待结果时,在另一线程上异步执行函数,可如下做:
1) 使用 std::async() 启动新线程来执行特定函数。创建异步生产者并返回与之关联的 future。为了保证函数异步执行,将 std::launch::async 策略作为第一个参数传递给函数:
auto f = std::async(std::launch::async, do_something);

2) 继续执行当前线程:
do_something_else();

3) 当你需要保证异步操作执行完毕时,在 std::async() 返回的 future 对象上调用 wait() 方法:
f.wait();

在当前线程继续执行直到线程需要来自异步函数的结果时,在工作线程上异步执行函数,可以执行以下操作:
1) 使用 std::async() 启动新线程来执行特定函数,创建异步生产者并返回与之关联的 future。为了保证函数异步执行,将 std::launch::async 策略作为第一个参数传递给函数:
auto f = std::async(std::launch::async, compute_something);

2) 继续执行当前线程:
auto value = compute_something_else();

3) 当你需要异步函数执行的结果时,可在 std::async() 返回的 future 对象上调用 get() 方法:
value += f.get();

C++  std::async()和future的工作原理

std::async() 变长参数函数模板有两个重载:一个指定启动策略作为第一个参数,另一个则不是。std::async() 的其他参数为执行的函数和对应的参数。启动策略由有作用域的枚举 std::launch 定义,在 <future> 头文件中可用:
enum class launch : /* unspecified */
{
    async = /* unspecified */,
    deferred = /* unspecified */,
    /* implementation-defined */
};
这两个启动策略指定了以下内容:
当标志(std::launch::async|std::launch::deferred)都被指定时,任务在新线程被异步执行还是在当前线程同步执行,取决于实现。这是 std::async() 重载的行为,它不指定启动策略,这个行为是不确定的。

不要使用不确定的 std::async() 重载来异步运行任务。因此,总是使用要求启动策略的重载,并总是只使用 std::launch::async。

std::async() 的两个重载都返回一个 future 对象,future 对象指向由 std::async() 为其建立的 promise-future 通道内部创建的共享状态。当你需要异步操作的结果时,在 future 上调用 get() 方法。这将阻塞当前线程直到结果值或异常可用。如果 future 不传递任何值或你对值不感兴趣,但你想要保证异步操作在某个时刻完成,则可以使用 wait() 方法,它会阻塞当前线程直到可通过 future 获取共享状态。

future 类还有两个额外的等待方法:
这些方法可被用于创建轮询程序,为用户显示状态,如下示例所示:
auto f = std::async(std::launch::async, do_something);

while(true)
{
    using namespace std::chrono_literals;
    auto status = f.wait_for(500ms);

    if(status == std::future_status::ready)
        break;

    std::cout << "waiting..." << '\n';
}

std::cout << "done!" << '\n';
此程序的运行结果如下:

waiting...
waiting...
waiting...
waiting...
operation 1 done
done!

相关文章