Ответ 1
Этот шаблон является удобным способом убедиться, что CancellationTokenRegistration.Unregister()
вызывается автоматически. Он часто используется Стивеном Тубом в его Parallel Programming with.NET блоге, например. здесь.
Я понимаю, что вы должны убедиться, что у вас есть IDisposable, но почему он даже реализует IDisposable? какие ресурсы он должен выпуск? Единственные методы, которые он считает равенством.
IMO, лучший ответ на это можно найти в .NET 4 Cancellation Framework от Microsoft Майк Лидделл:
Когда обратный вызов зарегистрирован на
CancellationToken
, текущий потокExecutionContext
захватывается так, что обратный вызов будет запущен с тем же самым контекстом безопасности. Захват может быть запрошен текущий контекст синхронизации потока через перегрузкуct.Register()
, если требуется. Обратные вызовы обычно сохраняется, а затем запускается при отмене запроса, но если обратный вызов регистрируется после того, как была запрошена аннулирование, обратный вызов будет немедленно запускать текущий поток или черезSend()
на текущийSynchronizationContext
если применимо.Когда обратный вызов зарегистрирован на
CancellationToken
, возвращается объект имеет значениеCancellationTokenRegistration
. Это тип легкой структуры то естьIDiposable
, и удаление этого объекта регистрации вызывает обратный вызов для снятия с учета. Гарантируется, что послеDispose()
вернулся, зарегистрированный обратный вызов не является и впоследствии не начнется. Следствием этого является то, чтоCancellationTokenRegistration.Dispose()
должен блокироваться, если обратный вызов в настоящее время выполняется. Следовательно, все зарегистрированные обратные вызовы должны быть быстрыми и не блокировать какую-либо значительную продолжительность.
Другим релевантным документом Майка Лидделла является "Использование поддержки аннулирования в .NET Framework 4" (UsingCancellationinNET4.pdf).
Обновлено, это проверяется здесь, в исходном источнике.
Также важно отметить, что обратный вызов отмены регистрируется с помощью CancellationTokenSource
, а не с CancellationToken
. Таким образом, если CancellationTokenRegistration.Dispose()
не имеет корректной области действия, регистрация будет оставаться активной для жизни родительского объекта CancellationTokenSource
. Это может привести к неожиданному обратному вызову, когда объем операции async завершен, например:
async Task TestAsync(WebClient wc, CancellationToken token)
{
token.Register(() => wc.CancelAsync());
await wc.DownloadStringAsync(new Uri("http://www.hamster.com"));
}
// CancellationTokenSource.Cancel() may still get called later,
// in which case wc.CancelAsync() will be invoked too
Таким образом, важно объединить одноразовое CancellationTokenRegistration
с помощью using
(или вызвать CancellationTokenRegistration.Dispose()
явно с помощью try/finally
).