如何统计带回调的多异步函数调用总执行耗时?
异步方法批量调用的总执行耗时统计方案
同步方法的耗时统计(参考示例)
当DoWork()是同步方法时,统计n次调用的总耗时非常直接,只需在循环前后记录时间即可:
// File: PrepareForWork.cpp Timer startTime = GetCurrentTime(); for (int i = 0; i < n; i++) { workerObj->DoWork(); } cout << "Total execution time: " << GetCurrentTime() - startTime << endl;
// File: Worker.cpp Worker::DoWork() { // 执行具体任务逻辑 }
异步方法的统计难点
当方法改为带完成回调CallBackOnComplete()的异步方法DoWorkAsync()时,任务执行与回调逻辑分散在不同文件,且异步任务是并发执行的——循环会立刻结束,但此时所有异步任务可能仍在运行,无法直接用同步方式统计总耗时。
原异步示例代码:
// File: PrepareForWork.cpp for (int i = 0; i < n; i++) { workerObj->DoWorkAsync(); }
// File: Worker.cpp Worker::DoWorkAsync() { // 触发异步任务执行 } Worker::CallBackOnComplete(Data* outputData) { // 任务完成后的回调逻辑 }
可行解决方案
方案1:线程安全计数器+共享计时器
核心逻辑:在发起异步任务前记录起始时间,用线程安全的计数器跟踪已完成任务数,当最后一个任务的回调触发时,计算总耗时。
修改后代码实现
- PrepareForWork.cpp
#include <atomic> std::atomic<int> completedTasks = 0; Timer globalStartTime; int main() { int n = 10; // 示例调用次数 globalStartTime = GetCurrentTime(); for (int i = 0; i < n; i++) { workerObj->DoWorkAsync(); } // 实际项目建议用条件变量替代空循环,避免CPU空转 while (completedTasks < n) {} cout << "Total execution time: " << GetCurrentTime() - globalStartTime << endl; return 0; }
- Worker.cpp
#include "PrepareForWork.h" // 引入计数器与起始时间的声明 Worker::CallBackOnComplete(Data* outputData) { // 原有回调逻辑 // ... int completed = ++completedTasks; // 最后一个任务完成时计算总耗时 if (completed == n) // n需保证跨文件可见(比如全局常量或传递参数) { cout << "Total execution time (callback): " << GetCurrentTime() - globalStartTime << endl; } }
方案2:传递任务上下文(更优雅的封装)
避免全局变量,给每个异步任务传递包含起始时间、总任务数、完成计数器的上下文对象,在回调中统一统计。
修改后代码实现
- 公共头文件定义上下文结构体
#include <atomic> struct TaskContext { Timer startTime; int totalTasks; std::atomic<int>* completedCount; };
- PrepareForWork.cpp
int main() { int n = 10; std::atomic<int> completedCount = 0; Timer startTime = GetCurrentTime(); TaskContext ctx{startTime, n, &completedCount}; for (int i = 0; i < n; i++) { workerObj->DoWorkAsync(&ctx); } while (completedCount < n) {} cout << "Total execution time: " << GetCurrentTime() - startTime << endl; return 0; }
- Worker.cpp
Worker::DoWorkAsync(TaskContext* ctx) { // 触发异步任务时携带上下文,确保回调能获取到 startAsyncTask([ctx](Data* data) { CallBackOnComplete(data, ctx); }); } Worker::CallBackOnComplete(Data* outputData, TaskContext* ctx) { // 原有回调逻辑 // ... int completed = ++(*ctx->completedCount); if (completed == ctx->totalTasks) { cout << "Total execution time: " << GetCurrentTime() - ctx->startTime << endl; } }
注意事项
- 必须用线程安全的计数器(如
std::atomic),因为回调可能在不同线程触发。 - 主线程的等待逻辑,实际项目中推荐用
std::condition_variable替代空循环,避免不必要的CPU消耗。 - 需确保上下文对象的生命周期覆盖所有异步任务的回调执行周期,避免野指针问题。
内容的提问来源于stack exchange,提问作者ontherocks
相关产品推荐
相关产品推荐

