异步过程调用被另一个线程中断

Asynchronous Procedure Call interrupted by another thread?

本文关键字:中断 线程 另一个 异步过程调用      更新时间:2023-10-16

我刚刚意识到我的APC被另一个线程中断了。所以在这一点上,我想知道这怎么可能。正如我所理解的APC的概念:

  • APC不能被正常的用户模式线程中断
  • 一旦QueueUserAPC()请求APC,就会立即调度它
  • APC具有运行完成语义

这是我最初的情况:我有一个多线程应用程序(1个主线程,1个接收线程)。优先级较低的接收线程正在从外部源接收数据。接收线程也通过使用WaitForSingleObject()来重新激活。接收到的数据被存储在共享的CCD_ 2中。主线程实际上什么也不做。每隔x毫秒就会发生一个特殊事件,导致接收线程调度APC。这个APC是在主线程的上下文中运行的。

这里有一个关于整个情况的伪源代码示例。

class Example {
public:
  Example(void) {
    ::DuplicatHandle(
        ::GetCurrentProcess(),
        ::GetCurrentThread(),
        ::GetCurrentProcess(),
        &m_mainthreadHandle,
        THREAD_SET_CONTEXT,
        FALSE,
        0);
  }
  void run(void) {
    ::WaitForSingleObjectEx(m_apcActivation);
  }
protected:
private:
  void rxThread(void) {
    // this seems to be called during the apcRoutine() is running
    // as result the shared list m_rxList is corrupted!
    ::WaitForSingleObject(m_externalActivation);
    Data data = externalReceive();
    if(data.attribute == SPECIAL) {
      ::QueueUserAPC(apcRoutine, m_mainthreadHandle, 0);
    } else {
      m_rxList.push_front(data);
    }
  }
  void apcRoutine(void) {
    while(!m_rxList.empty()) {
      m_rxList.front().print();
      m_rxList.pop_front();
    }
  }

  std::list<Data> m_rxList;
  HANDLE          m_externalActivation;
  HANDLE          m_apcActivation;
  HANDLE          m_mainthreadHandle;
};

void main(void) {
  Example e;
  e.run();
}

我的问题是:接收线程的WaitForSingleObject()是否可能中断APC?如果是,为什么?

您似乎对APC有一些误解。

有两种(实际上是三种)APC,usermode和kernelmode(具有两种不同的优先级)APC。

用户模式APC,比如你在代码中使用的APC,其行为与内核模式APC不同。特别是:

  • 它们不会抢占线程
  • 当您调用QueueUserAPC时,它们不会立即运行,除非目标线程已经在可报警等待中被阻塞。相反,APC被排队
  • 否则,它们仅在线程进入可报警等待或调用NtTestAlert时运行
  • 它们并不是不可抢占的,事实上,它们本身并没有什么"特别"之处。它们只是回调,您可以对线程进行排队,并在定义明确的时间进行调用
  • 它们不保证运行到完成1,但保证按顺序执行
  • 没有办法强制另一个线程执行其排队的APC。目标线程必须使自己能够为其APC的运行发出警报。尽管存在一个未记录的函数NtAlertThread,但它并不像人们所期望的那样工作,NtAlertResumeThread也不工作

另一方面,内核模式APC抢占用户模式线程,并且不可被它们中断(APC_LEVEL APC也不可被PASSIVE_LEVEL APC中断)。


1如果情况也是如此,那将是非常荒谬的。这样,您就可以使用APC完全颠覆调度程序,消耗不确定的CPU时间——无论进程优先级、特权或配额如何。