Какие обстоятельства кроме "ожидают" позволят прерывать синхронный код
Недавно я обнаружил странную ошибку в моем асинхронном коде. Я вызывал блокирующий метод для 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++, который не имеет понятия async/await. Однако это элемент управления Active-X.
Я не знаю деталей реализации.Net RCW, но я предполагаю, что должна выполняться какая-то прокачка сообщений, чтобы позволить элементу управления работать. Я также предполагаю, что эта накачка позволяет моим продолжениям работать.
Верны ли мои предположения? И есть ли какие-либо другие обстоятельства, о которых я должен знать, где мой предположительно синхронный код может быть прерван?
2 ответа
Это вполне нормально, COM прокачает, когда вызов пересекает границу квартиры. Он должен прокачивать, если этого не сделать, это может привести к тупику. Непонятно, почему эту границу нужно пересечь из фрагмента, это похоже на поток STA, когда вы говорите о WPF. Это может быть сервер вне процесса, возможно, вы создали объект в рабочем потоке.
Он принципиально не отличается от способа, которым CLR будет прокачивать вызовы WaitOne() или Join(), когда вы блокируете поток STA. Головные боли повторного входа, которые это вызывает, очень похожи на страдания, вызванные DoEvents(). И COM, и CLR откачивают выборочно, но не так опасно. Хотя очень недокументированный.
Квартиры COM значительно упрощают нарезку, решая 98% обычных проблем. Последние 2% дают вам расщепленную мигрень, которую очень трудно решить, повторный вход стоит на вершине этого списка. Единственный способ решить эту проблему - не оставлять это на усмотрение COM, чтобы предоставить компоненту многопоточный дом и позаботиться о нем самостоятельно. С таким кодом.
Независимо от ожидания или нет, задание вернулось из DoAsync
уже началось, когда метод возвращается.
И вы используете задержку всего 1 миллисекунду.
Вы пробовали большую задержку?