在异步API (c++)中,如何保证API在调用回调之前返回
How to guarantee that the API returns before its callback is invoked in asyncronous API (c++)
我开发了一个具有异步api的库。其中一个api,当它被调用时,它创建一个任务,将其推送到任务线程并返回其任务ID。任务完成后,任务线程通过调用回调函数将结果通知给调用者。
顺序如下
调用者C.1。调用者调用API
C.2。自由。创建任务并将其推送到任务线程的队列
C.3。自由。通过调用condition_variable
的notify_all来唤醒任务线程C.4。此时,可以进行上下文切换,该线程将被挂起
C.6。这个线程恢复后,Lib。返回任务ID
任务线程
T.1。任务线程执行任务。
T.2。当任务完成时,任务线程通过调用回调
将结果通知调用者。调用者根据任务ID检查回调函数的结果数据,但偶尔在API返回之前调用回调函数,调用者无法检查结果。
我想完美地保证API在调用回调之前返回任务ID。我能做什么?
我在API的主体中使用lock_guard来防止回调被调用,这大大减少了再现此问题的可能性。
但是因为lock_guard在API返回之前解锁互斥对象,如果上下文切换发生在互斥对象解锁之后,在API返回之前,这个问题可以很少重现。
我也想防止这种情况。
总结规范
long AcbCore::setState(long appState, long playState) // API
{
StateTask* task = (StateTask*) createTask(TaskType::PLAYER_STATE_CHANGE, &isAppSwitchingStateFlag, appState);
std::lock_guard<std::mutex> lockGd (*task->getEventMutex());
pushTask(task); // at this position, context switch can occur by condTaskThread_.notify_all() of resumeLoop()
return taskId;
}
void AcbCore::pushTask(Task* task)
{
mtxTaskQueue_.lock();
queueTask_.push_back(task);
mtxTaskQueue_.unlock();
resumeLoop();
}
void AcbCore::resumeLoop()
{
mtxTaskThread_.lock();
mtxTaskThread_.unlock();
condTaskThread_.notify_all();
}
bool AcbCore::suspendLoop()
{
bool isTimeout = false;
if (ingTask_ != NULL) {
isTimeout = (condTaskThread_.wait_for(lockTaskThread_, std::chrono::seconds(AWAKE_TASK_THREAD_TIMEOUT)) == std::cv_status::timeout);
} else {
condTaskThread_.wait(lockTaskThread_);
}
return isTimeout;
}
void AcbCore::taskLoop() // loop of Task Thread
{
Task* task = NULL;
Action* action = NULL;
while (isInitialized_) {
while (popTask(task)) {
if (task->isCompleted()) {
fireEvent(task);
} else {
doNextTask(task);
}
}
if (suspendLoop()) { // if awaked by timeout
cancelTask(ingTask_, true);
}
}
}
void AcbCore::fireEvent(Task* task, bool bDelete)
{
std::string errorInfo = task->getErrorInfo();
task->waitToUnlockEvent();
// eventHandler_ : callback set by caller when Acb is initialized
eventHandler_(task->getTaskId(), task->getEventType(), appState_.now_, playState_.now_, errorInfo.c_str());
if (bDelete) {
deleteTask(task);
}
}
从根本上说,你无法解决这个问题。让我们假设初始线程正好在代码的最后一条指令之后挂起,但在调用者获得任务ID之前。您根本无法确定调用者何时以允许发生回调的方式存储了任务ID。
相反,客户端应该缓存未知的任务id,当他有一个未完成的调用你的异步函数。
相关文章:
- 使用Qt框架在c ++类中创建API调用
- 程序在对mouse_event的 Windows API 调用中冻结
- 使用 Node.js N-API 调用 C 函数时,不会显示预期的输出
- 为什么创建进程 API 调用会导致内存访问冲突?
- 引发访问冲突的 Win32 API 调用
- 父进程中的挂钩 api 调用
- 如何监视应用程序进行的Windows系统API调用?
- UNIX API 调用:使用 read() 函数打开文件并将其打印到屏幕上会增加额外的随机字符
- API调用以获取当前的过程大小
- 有没有办法使用 win32 API 调用获取 MachineGuid(而不是从注册表中读取它)
- 当用户更改密码时,哪个窗口 API 调用?钩住这个 API 的好方法是什么
- 当我运行此代码时,它"stopped working"。我希望从 API 调用这个简单的函数
- 解密MSDN C API调用和结构,并从VB6调用
- 按下Enter按钮时,如何使用C Win32 API调用按钮
- 错误 C1090:PDB API 调用失败,错误代码'0':"
- Oracle 11gR1或R2客户端上是否设置了在ODBC API调用中将SELECT COUNT(*)返回的数据类型从
- Unix C++第三方API调用
- 在程序中跟踪 Win32 API 调用C++
- 这个Windows API调用WaitForSingleObject有什么问题?
- 如何在拥有 bost POST 数据和标头数据的同时与卡萨布兰卡执行 API 调用