繁体   English   中英

C++:使用 future.get 超时且无阻塞

[英]C++: Use future.get with timeout and without blocking

我遇到了这个问题,我有一个主循环,需要触发异步工作并且不能等待它完成。 我想要它做的是检查每个 while 循环是否完成了异步工作。 这可以通过 future.wait_for() 来完成。

因为我不想阻塞主循环,所以我可以使用 future.wait_for(0)。 到目前为止,一切都很好。

此外,我想验证我是否在 X 毫秒内收到(或未收到)答复。 我可以通过检查自启动“异步”以来多长时间来做到这一点,并验证先发生的事情 - X ms 通过或 future_status::ready 返回。

我的问题 - 这是一个好的做法,还是有更好的方法?

更多信息:由于主循环必须启动许多不同的异步作业,这意味着我需要有很多重复的代码 - 每次启动都需要“记住”它启动的时间戳以及每次我检查异步作业是否准备就绪,我需要重新计算每个异步作业的时间差。 这可能会很麻烦。

现在 - 这是我所描述的示例(可能有构建错误):

#define MAX_TIMEOUT_MS 30
bool myFunc()
{
    bool result = false;
    //do something for quite some time
    return result;
}

int main()
{
    int timeout_ms = MAX_TIMEOUT_MS;
    steady_clock::time_point start;
    bool async_return = false;
    std::future_status status = std::future_status::ready;
    int delta_ms = 0;

    while(true) {
        // On first time, or once we have an answer, launch async again
        if (status == std::future_status::ready) {
            std::future<bool> fut = std::async (std::launch::async, myFunc);
            start = steady_clock::now();        // record the start timestamp whenever we launch async()
        }

        // do something...

        status = fut.wait_for(std::chrono::seconds(0));
        // check how long since we launched async
        delta_ms = chrono::duration_cast<chrono::milliseconds>(steady_clock::now() - start).count();

        if (status != std::future_status::ready && delta_ms > timeout_ms ) {
            break;
        } else {
            async_return = fut.get();
            // and we do something with the result
        }
    }

    return 0;
}

您可能需要考虑一件事:如果您的 while 循环不执行任何相关工作,而只是检查任务是否完成,则您可能正在忙等待 ( https://en.wikipedia.org/wiki/Busy_waiting ) . 这意味着您正在浪费大量 CPU 时间来做无用的工作。 这听起来可能违反直觉,但它会对您评估任务完成情况的表现产生负面影响,即使您一直在检查它!

之所以会发生这种情况,是因为该线程看起来像是在为操作系统做大量工作,并且会获得高优先级进行处理。 这可能会使其他线程(正在执行您的异步作业)看起来不那么重要并且需要更长的时间才能完成。 当然,这不是一成不变的,任何事情都有可能发生,但是,如果您在该循环中不做任何其他工作,这仍然是 CPU 的浪费。

wait_for(0) 不是最佳选择,因为它有效地中断了该线程的执行,即使工作尚未准备好。 它恢复工作所需的时间可能比您预期的要长 ( https://en.cppreference.com/w/cpp/thread/future/wait_for )。 std::future 似乎还没有真正的非阻塞 API( C++ 异步编程,如何不等待未来? ),但您可以使用其他资源,例如互斥锁和 try_lock( http://www .cplusplus.com/reference/mutex/try_lock/ )。

也就是说,如果您的循环仍在执行重要工作,则可以使用此流程。 但是您可能想要检查一个已完成作业的队列,而不是一个单一的未来。 该队列只会被您的主线程使用,并且可以通过非阻塞线程安全的“try_get”调用来实现,以获取下一个完成的作业。 正如其他人评论的那样,您可能希望将节省时间的逻辑包装在作业调度程序 class 或类似程序中。

也许是这样的(伪代码:):

struct WorkInfo {
  time_type begin_at; // initialized on job dispatch
  time_type finished_at;
  // more info
};

thread_safe_vector<WorkInfo> finished_work;

void timed_worker_job() {
  info.begin_at = current_time();
  
  do_real_job_work();
  
  WorkInfo info;
  info.finished_at = current_time();
  finished_work.push(some_data);
}

void main() {
   ...
   
   while (app_loop)
   {
       dispatch_some_jobs();

       WorkInfo workTemp;
       while (finished_work.try_get(&work)) // returns true if returned work
       {
         handle_finished_job(workTemp);
       }
   }
   
   ...
}

如果您不熟悉,我还建议您阅读线程池 ( https://en.wikipedia.org/wiki/Thread_pool ) 和生产者-消费者 (https://en.wikipedia.org/wiki/Producer )%E2%80%93消费者问题)。

下面的代码异步运行任务并稍后检查它们是否完成。

我添加了一些假工作并等待查看结果。

#define MAX_TIMEOUT_MS 30

struct fun_t {
    size_t _count;
    bool finished;
    bool result;
    fun_t () : _count (9999), finished (false), result (false) {
    }
    fun_t (size_t c) : _count (c), finished (false), result (false) {
    }
    fun_t (const fun_t & f) : _count (f._count), finished (f.finished), result (f.result) {
    }
    fun_t (fun_t && f) : _count (f._count), finished (f.finished), result (f.result) {
    }
    ~fun_t () {
    }
    const fun_t & operator= (fun_t && f) {
        _count = f._count;
        finished = f.finished;
        result = f.result;
        return *this;
    }
    void run ()
    {
        for (int i = 0; i < 50; ++i) {
            cout << _count << " " << i << endl;;
            std::this_thread::sleep_for(std::chrono::milliseconds(100));
        }
        result = true;
        finished = true;
        cout << "       results: " << finished << ", " << result << endl;
    }
    operator bool () { return result; }
};

int main()
{
    int timeout_ms = MAX_TIMEOUT_MS;
    chrono::steady_clock::time_point start;
    bool async_return = false;
    std::future_status status = std::future_status::ready;
    int delta_ms = 0;
    std::map<size_t, fun_t> futs;
    std::vector<std::future<void>> futfuncs;

    size_t count = 0;
    bool loop = true;

    cout << "Begin --------------- " << endl;

    while (loop) {
        loop = false;
        // On first time, or once we have an answer, launch async again
        if (count < 3 && status == std::future_status::ready) {
            //std::future<bool> fut = std::async (std::launch::async, myFunc);
            futs[count] = std::move(fun_t(count));
            //futs[futs.size() - 1].fut = std::async (std::launch::async, futs[futs.size() - 1]);
            futfuncs.push_back (std::move(std::async(std::launch::async, &fun_t::run, &futs[count])));
        }

        // do something...
        std::this_thread::sleep_for(std::chrono::seconds(2));

        for (auto & f : futs) {
            if (! f.second.finished) {
                cout << " Not finished " << f.second._count << ", " << f.second.finished << endl;
                loop = true;
            } else {
                bool aret = f.second;
                cout << "Result: " << f.second._count << ", " << aret << endl;;
            }
        }

        ++count;
    }

    for (auto & f : futs) {
        cout << " Verify " << f.second._count << ", " << f.second.finished;
        if (f.second.finished) {
            bool aret = f.second;
            cout << "; result: " << aret;
        }
        cout << endl;
    }

    cout << "End --------------- " << endl;
    return 0;
}

删除行(太多)后,您会看到任务。 第一个数字是任务 ID,第二个是迭代编号。

Begin --------------- 
0 0
0 1
0 2
 Not finished 0, 0
1 0
0 20
1 1 
 Not finished 0, 0
 Not finished 1, 0
2 0
1 20
0 40
2 1
0 49   // here task 0 ends
2 10
1 30
       results: 1, 1  // "run" function ends
1 39
Result: 0, 1         // this is the verification "for"
 Not finished 1, 0
 Not finished 2, 0
       results: 1, 1
Result: 0, 1
Result: 1, 1
Result: 2, 1
 Verify 0, 1; result: 1
 Verify 1, 1; result: 1
 Verify 2, 1; result: 1
End --------------- 

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM