异步完成处理

Asynchronous Completion Handling

本文关键字:处理 异步      更新时间:2023-10-16

>我有这种情况:

void foo::bar()
{
    RequestsManager->SendRequest(someRequest, this, &foo::someCallback);
}

其中 RequestsManager 以异步方式工作:

  • 发送请求将请求放入队列并返回给调用方
  • 其他线程从队列中获取请求并处理它们
  • 处理一个请求时,将调用回调

是否可以在与 SendRequest 相同的线程中调用 foo::someCallback?如果没有,我如何避免遵循"回调限制":回调不应进行耗时的操作,以避免阻塞请求管理器。

否 -

调用/回调不能更改线程上下文 - 您必须发出一些信号才能在线程之间进行通信。

通常,"someCallback"会发出发出"SendRequest"调用的线程

正在等待的事件的信号(同步调用),或者将SendRequest(因此,可能是其处理的结果)推送到队列中,发起"SendRequest"调用的线程最终将弹出(异步)。 只看发起人如何发出信号。.

Aynch 示例 - 回调可能 PostMessage/Dispatcher.Begin将已完成的 SendRequest 调用到 GUI 线程以显示结果。

我可以看到几种实现它的方法:

A) 实施类似于信号处理的策略

当请求处理结束时RequestManager将回调调用放在等待列表中。下次调用SendRequest时,在返回执行之前,它将检查线程是否有任何待处理的回调并执行它们。这是一种相对简单的方法,对客户端的要求最低。如果延迟不是问题,请选择它。 RequestManager可以公开 API 以强制检查挂起的回调

B) 暂停回调目标线程并在第三个线程中执行回调

这将为您提供真正的异步解决方案及其所有注意事项。看起来目标线程执行被中断,执行跳转到中断处理程序。在回调返回之前,需要恢复目标线程。您将无法从回调内部访问线程本地存储或原始线程的堆栈。

取决于"耗时的操作"的定义。

执行此操作的经典方法是:

  • 处理请求时,RequestManager应执行该&foo::someCallback
  • 为了避免阻止请求管理器,您可以在此回调中举起一个标志
  • 定期检查线程内部的该标志,该标志称为RequestsManager->SendRequest
  • 这面旗帜将只是class foo内部的一个volatile bool

如果要确保调用线程(foo)立即理解,request已被处理,则需要额外的同步。

在这些线程之间实现(或使用已经实现的)阻塞管道(或使用信号/事件)。这个想法是:

  • foo 的线程执行SendRequest
  • foo开始睡在某个select上(例如)
  • RequestManager执行请求并:
    • 呼叫&foo::someCallback
    • "唤醒"foo的线程(通过在该文件描述符中发送一些内容,foo睡眠(使用 select ))
  • foo觉醒了
  • 检查已处理请求的volatile bool标志
  • 做它需要做的事情
  • 取消国旗