Относится к http://blogs.msdn.com/b/pfxteam/archive/2012/02/12/10266988.aspx, http://winrtstoragehelper.codeplex.com/, магазину приложений Windows 8 и .net 4.5
Вот мой взгляд на это:
Функция языка async / await делает многие вещи довольно простыми, но она также представляет сценарий, который был
редко встречался до того, как стало так легко использовать асинхронные вызовы: reentrance.
Это особенно верно для обработчиков событий, потому что для многих событий вы не имеете никакого представления о том, что происходит после того, как вы вернетесь из обработчика событий.
В действительности может произойти то, что асинхронный метод, который вы ожидаете в первом обработчике событий, вызывается из другого обработчика событий, все еще находящегося на
та же нить.
Вот реальный сценарий, с которым я столкнулся в приложении Windows 8 App Store:
Мое приложение имеет два фрейма: входя и выходя из фрейма, я хочу загрузить / сохранить некоторые данные в файл / хранилище.
События OnNavigatedTo / From используются для сохранения и загрузки. Сохранение и загрузка выполняются с помощью некоторой функции асинхронной утилиты (например, http://winrtstoragehelper.codeplex.com/).
При переходе от кадра 1 к кадру 2 или в другом направлении вызывается и ожидается асинхронная загрузка и безопасные операции.
Обработчики событий становятся асинхронными, возвращая void => их нельзя ожидать.
Однако первая операция открытия файла (скажем, внутри функции сохранения) утилиты также выполняется асинхронно
и поэтому первое ожидание возвращает управление платформе, которая через некоторое время вызывает другую утилиту (нагрузку) через второй обработчик событий.
Теперь загрузка пытается открыть тот же файл, и если
файл уже открыт для операции сохранения, происходит сбой с исключением ACCESSDENIED.
Минимальное решение для меня - обеспечить доступ к файлам с помощью использования и AsyncLock.
private static readonly AsyncLock m_lock = new AsyncLock();
...
using (await m_lock.LockAsync())
{
file = await folder.GetFileAsync(fileName);
IRandomAccessStream readStream = await file.OpenAsync(FileAccessMode.Read);
using (Stream inStream = Task.Run(() => readStream.AsStreamForRead()).Result)
{
return (T)serializer.Deserialize(inStream);
}
}
Обратите внимание, что его блокировка в основном блокирует все операции с файлами для утилиты всего одной блокировкой, которая излишне сильна, но отлично работает для моего сценария.
Здесь - мой тестовый проект: приложение магазина приложений для Windows 8 с некоторыми тестовыми вызовами для исходной версии http://winrtstoragehelper.codeplex.com/ и моя модифицированная версия, использующая AsyncLock от Stephen Toub http://blogs.msdn.com/b/pfxteam/archive/2012/02/12/10266988.aspx.
Могу ли я также предложить эту ссылку:
http://www.hanselman.com/blog/ComparingTwoTechniquesInNETAsynchronousCoordinationPrimitives.aspx