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

时间:2013-11-22 19:57:17

标签: c++ multithreading apc

我刚刚意识到我的APC被另一个线程打断了。 所以在这一点上我想知道这是怎么回事。 据我了解APC的概念:

  • APC不能被正常的用户模式线程打断
  • 在QueueUserAPC()
  • 请求后立即调度APC
  • APC具有运行到完成语义

所以这是我最初的情况: 我有一个多线程应用程序(1个主线程,1个接收线程)。 较低优先级的接收线程是从外部源接收数据。 使用WaitForSingleObject()也可以重新激活接收线程。 收到的数据存储在共享的std::list中。 主线程实际上没有任何效果。 每隔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? 如果是,为什么?

1 个答案:

答案 0 :(得分:5)

您似乎对APC存在一些误解。

有两种(实际上是三种)APC,用户模式和内核模式(具有两个不同的优先级)APC。

Usermode APC(例如您在代码中使用的APC)的行为与kernelmode APC的行为方式不同。特别是:

  • 他们不会抢先一线,
  • 当您调用QueueUserAPC时,它们不会立即运行,除非目标线程已在可警告的等待中被阻止。相反,APC 排队
  • 否则只有在线程进入可警告的等待或调用NtTestAlert时才会运行它们。
  • 他们并非不可抢占,事实上对他们来说没有什么本质上的“特殊”。它们只是回调,你可以排队到一个线程,并在明确定义的时间调用它们。
  • 他们不保证完成 1 ,但他们保证按顺序执行。
  • 无法强制另一个线程执行其排队的APC。目标线程必须使其自身可以警告其APC运行。虽然存在未记录的函数NtAlertThread,但它并不像人们期望的那样工作,NtAlertResumeThread也没有。
另一方面,

Kernelmode APC会抢占用户模式线程并且不会被它们中断(并且APC_LEVEL APC也不会被PASSIVE_LEVEL APC中断)。

<小时/> 1 如果情况确实如此,那将是非常荒谬的。这样,您可以使用APC完全破坏调度程序,消耗不确定的CPU时间 - 无论进程优先级,权限或配额如何。