0

我有这种情况:

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

RequestsManager 以异步方式工作的地方:

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

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

4

3 回答 3

3

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

通常,“someCallback”会发出一个事件信号,在该事件上发起“SendRequest”调用的线程正在等待(同步调用),或者将 SendRequest(因此,可能是其处理的结果)推送到队列中发起“SendRequest”调用的线程最终将弹出,(异步)。仅取决于如何向发起者发出信号..

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

于 2012-08-14T10:59:19.720 回答
1

取决于“耗时操作”的定义。

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

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

如果您想确保调用线程 ( foo's) 将立即理解request已处理,您需要额外的同步。

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

  • foo的线程执行SendRequest
  • foo开始睡一些select(例如)
  • RequestManager执行请求并且:
    • 来电&foo::someCallback
    • “唤醒”foo的线程(通过在该文件描述符中发送一些东西,该文件描述符foo(使用select)休眠)
  • foo被唤醒
  • 检查volatile bool已处理请求的标志
  • 做它需要做的事
  • 取消国旗
于 2012-08-14T11:01:46.657 回答
1

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

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

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

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

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

于 2012-08-14T11:04:19.497 回答