有没有办法在C++ 11中取消/分离未来?

m42*_*42a 31 c++ future c++11

我有以下代码:

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

using namespace std;

int sleep_10s()
{
    this_thread::sleep_for(chrono::seconds(10));
    cout << "Sleeping Done\n";
    return 3;
}

int main()
{
    auto result=async(launch::async, sleep_10s);
    auto status=result.wait_for(chrono::seconds(1));
    if (status==future_status::ready)
        cout << "Success" << result.get() << "\n";
    else
        cout << "Timeout\n";
}
Run Code Online (Sandbox Code Playgroud)

这应该等待1秒,打印"超时",然后退出.它不再退出,而是再等9秒,打印"Sleep Done",然后是段错误.有没有办法取消或分离未来所以我的代码将在main结束时退出而不是等待将来完成执行?

Ant*_*ams 26

C++ 11标准没有提供取消启动任务的直接方法std::async.您必须实现自己的取消机制,例如将原子标志变量传递给定期检查的异步任务.

你的代码不应该崩溃.到达结束时main,std::future<int>保留的对象将result被销毁,等待任务完成,然后丢弃结果,清理所使用的任何资源.

  • 从一个线程编写非原子变量,并从另一个线程读取该变量而不进行同步(例如,在写入和读取时锁定相同的互斥锁)是未定义的行为."它的效果就像我预期的那样,当我尝试它"是UB的一个可能的结果,但你可能在其他场合得到不同的结果(例如崩溃). (17认同)
  • 仅在写入期间使用互斥锁是不够的 - 您必须使用相同的互斥锁进行读写操作.如果它只是一个简单的布尔变量(就像这里),并且你无论如何都在使用C++ 11库,只需使用`std :: atomic <bool>`,编译器就会为你处理一切. (3认同)
  • 国旗真的必须是原子的吗?假如你有一个递归搜索函数,并且想要在其中一个找到结果时取消所有线程.即使刚刚从标志中读取`false`之后,但在处理该信息之前,在线程A中,成功的线程B将"true"写入该标志,没有真正的伤害,因为在下一个递归深度线程A仍然会取消自己.只要写入是单向的(仅从"false"到"true"),就不应该存在正确性问题,即使是同时写入也不存在.这个推理是否正确? (2认同)

bao*_*aol 17

这是一个使用原子bool同时取消一个或多个future的简单示例.原子bool可以包含在Cancellation类中(取决于品味).

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

using namespace std;

int long_running_task(int target, const std::atomic_bool& cancelled)
{
    // simulate a long running task for target*100ms, 
    // the task should check for cancelled often enough!
    while(target-- && !cancelled)
        this_thread::sleep_for(chrono::milliseconds(100));
    // return results to the future or raise an error 
    // in case of cancellation
    return cancelled ? 1 : 0;
}

int main()
{
    std::atomic_bool cancellation_token;
    auto task_10_seconds= async(launch::async, 
                                long_running_task, 
                                100, 
                                std::ref(cancellation_token));
    auto task_500_milliseconds = async(launch::async, 
                                       long_running_task, 
                                       5, 
                                       std::ref(cancellation_token));
// do something else (should allow short task 
// to finish while the long task will be cancelled)
    this_thread::sleep_for(chrono::seconds(1));
// cancel
    cancellation_token = true;
// wait for cancellation/results
    cout << task_10_seconds.get() << " " 
         << task_500_milliseconds.get() << endl;
}
Run Code Online (Sandbox Code Playgroud)


tbo*_*otz 5

我知道这是一个老问题,但在搜索时它仍然是“分离 std::future”的最佳结果。我想出了一个简单的基于模板的方法来处理这个问题:

template <typename RESULT_TYPE, typename FUNCTION_TYPE>
std::future<RESULT_TYPE> startDetachedFuture(FUNCTION_TYPE func) {
    std::promise<RESULT_TYPE> pro;
    std::future<RESULT_TYPE> fut = pro.get_future();

    std::thread([func](std::promise<RESULT_TYPE> p){p.set_value(func());},
                std::move(pro)).detach();

    return fut;
}
Run Code Online (Sandbox Code Playgroud)

你像这样使用它:

int main(int argc, char ** argv) {
    auto returner = []{fprintf(stderr, "I LIVE!\n"); sleep(10); return 123;};

    std::future<int> myFuture = startDetachedFuture<int, decltype(returner)>(returner);
    sleep(1);
}
Run Code Online (Sandbox Code Playgroud)

输出:

$ ./a.out 
I LIVE!
$
Run Code Online (Sandbox Code Playgroud)

如果 myFuture 超出范围并被破坏,线程将继续执行其正在执行的操作,而不会导致问题,因为它拥有 std::promise 及其共享状态。适合有时您只想忽略计算结果并继续前进的情况(我的用例)。

对于OP的问题:如果你到达main的末尾,它将退出而不等待未来完成。

这个宏是不必要的,但如果您要频繁调用它,可以节省键入时间。

// convenience macro to save boilerplate template code
#define START_DETACHED_FUTURE(func) \
    startDetachedFuture<decltype(func()), decltype(func)>(func)

// works like so:
auto myFuture = START_DETACHED_FUTURE(myFunc);
Run Code Online (Sandbox Code Playgroud)