[英]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.