1

我最近在我的异步代码中遇到了一个奇怪的错误。我在 COM 控件上调用了一个阻塞方法,这似乎允许我的异步继续在它阻塞时运行。

考虑示例代码(仅用于说明目的)

public async void Main()
{
    //assume some single threaded dispatcher. eg. WpfDispatcher

    Task doAsyncTask = DoAsync();

    Console.WriteLine("Start synchronous");
    CallSomeCOMControl();
    Console.WriteLine("End synchronous");

    await doAsyncTask;
}

public async Task DoAsync()
{
    Console.WriteLine("Start async");
    await Task.Delay(1);
    Console.WriteLine("End async");
}

在正常情况下,我希望得到以下输出:

Start async
Start synchronous
End synchronous
End async

我实际上看到的是:

Start async
Start synchronous
End async
End synchronous

现在我没有 COM 控件的源代码,但我知道这是一个非常古老的 C++ 控件,没有异步/等待的概念。然而,它是一个 Active-X 控件。

我不知道 .Net RCW 的实现细节,但我假设必须进行某种消息泵送才能使控件正常工作。我还假设这种泵送允许我继续运行。

我的假设正确吗?还有我应该知道的其他任何情况,我的所谓同步代码可能会被中断吗?

4

2 回答 2

3

这是很正常的,当呼叫越过公寓边界时,COM 会抽水。它必须抽水,不抽水很可能造成死锁。目前尚不清楚为什么必须从片段中跨越该边界,当您谈论 WPF 时,它看起来像一个 STA 线程。可能是进程外服务器,可能是您在工作线程上创建了对象。

它与阻塞 STA 线程时 CLR 在 WaitOne() 或 Join() 调用上的泵送方式没有根本不同。这引起的重入头痛与 DoEvents() 引起的痛苦非常相似。不过,COM 和 CLR 都有选择性地泵送,没有那么危险。尽管高度无证。

COM 单元极大地简化了线程,处理了 98% 的常见问题。最后 2% 给你一个分裂的偏头痛,它可能非常难以解决,重入是最重要的。解决这个问题的唯一方法是不要让 COM 为组件提供线程安全的 home 并自己处理它。使用这样的代码

于 2015-06-17T11:47:29.340 回答
0

无论是否等待,从DoAsync方法返回时返回的任务已经开始。

而且您使用的延迟仅为 1 毫秒。

您是否尝试过更大的延迟?

于 2015-06-17T11:14:05.947 回答