Это вызвано рекурсивной природой вызовов? Когда я изменяю все вызовы await на фактическое выполнение .Wait () для их завершения и, по существу, избавляюсь от асинхронного характера вызовов, мой код выполняется нормально.
Это действительно зависит -
Наиболее вероятный виновник был бы, если бы ваш абонент каким-то образом блокировал поток пользовательского интерфейса (через вызов Wait () и т. Д.). В этом случае стандартное поведение await заключается в захвате вызывающего контекста синхронизации и последующей публикации результатов в этом контексте.
Однако, если вызывающий объект использует этот контекст, вы можете получить тупик.
Это, скорее всего, тот случай, и он вызван этой строкой кода:
Data(string name, bool load) { if (load) { Load.Wait(); }
Этого можно легко избежать, явно указав код вашей библиотеки (например, этот класс XmlData) , а не , используя контекст синхронизации вызова. Обычно это требуется только для кода интерфейса пользователя. Избегая захвата, вы делаете две вещи. Во-первых, вы улучшаете общую производительность (часто радикально), а во-вторых, избегаете этого состояния мертвой блокировки.
Это можно сделать с помощью ConfigureAwait и изменив код следующим образом:
override async Task Load()
{
var file = await GetFileAsync.(...).ConfigureAwait(false);
var xmlDoc = await LoadXmlDocAsync(file).ConfigureAwait(false);
ProcessXml(xmlDoc);
}
Как говорится, я бы немного переосмыслил этот дизайн. Здесь действительно две проблемы.
Во-первых, вы помещаете вызов виртуального метода в свой конструктор, что довольно опасно, и его следует избегать, когда это возможно, поскольку это может привести к необычным проблемам.
Во-вторых, вы превращаете всю свою асинхронную операцию в синхронную операцию, помещая ее в конструктор вместе с блоком. Вместо этого я бы порекомендовал переосмыслить всю эту вещь.
Возможно, вы могли бы переработать это, чтобы сделать какую-то фабрику, которая возвращает ваши данные, загруженные асинхронно? Это может быть так же просто, как сделать общедоступный API-интерфейс для создания фабричного метода, который возвращает Task<Data>
, или даже универсальный метод public async Task<TData> Create<TData>(string name) where TData : Data
, который позволит вам сохранить конструкцию и загрузку асинхронными и полностью избежать блокировки.