Реализация асинхронного тайм-аута с использованием асинхронных / ожидающих конструкций бедного человека в.Net 4.0
мотивация
C# 5.0 асинхронные / ожидающие конструкции - это круто, но, к сожалению, Microsoft показала только кандидат на выпуск.NET 4.5 и VS 2012, и потребуется некоторое время, чтобы эти технологии получили широкое распространение в наших проектах.
В асинхронных методах Стефана Тауба , итераторах C# и Задачах я нашел замену, которую можно красиво использовать в.NET 4.0. Есть также дюжина других реализаций, которые делают возможным использование этого подхода даже в.NET 2.0, хотя они кажутся немного устаревшими и менее функциональными.
пример
Итак, теперь мой код.NET 4.0 выглядит так (прокомментированные разделы показывают, как это делается в.NET 4.5):
//private async Task ProcessMessageAsync()
private IEnumerable<Task> ProcessMessageAsync()
{
//var udpReceiveResult = await udpClient.ReceiveAsync();
var task = Task<UdpAsyncReceiveResult>
.Factory
.FromAsync(udpClient.BeginReceive, udpClient.EndReceive, null);
yield return task;
var udpReceiveResult = task.Result;
//... blah blah blah
if (message is BootstrapRequest)
{
var typedMessage = ((BootstrapRequest)(message));
// !!! .NET 4.0 has no overload for CancellationTokenSource that
// !!! takes timeout parameter :(
var cts
= new CancellationTokenSource(BootstrapResponseTimeout); // Error here
//... blah blah blah
// Say(messageIPEndPoint, responseMessage, cts.Token);
Task.Factory.Iterate(Say(messageIPEndPoint, responseMessage, cts.Token));
}
}
Выглядит немного некрасиво, хотя это делает работу
Вопрос
При использовании CancellationTokenSource в.NET 4.5 существует конструктор, который принимает временной интервал в качестве параметра времени ожидания, так что в результате CancellationTokenSource
отменяет в течение указанного периода времени.
.Net 4.0 не может тайм-аут, так как правильно сделать это в.Net 4.0?
3 ответа
Это действительно имеет отношение к async/await? Похоже, вам просто нужен способ отменить токен, независимо от async / await, верно? В этом случае, вы могли бы просто создать таймер, который вызывает Cancel после истечения времени ожидания?
new Timer(state => cts.Cancel(), null, BootstrapResponseTimeout, Timeout.Infinite);
РЕДАКТИРОВАТЬ
Мой первоначальный ответ, приведенный выше, является основной идеей, но более надежное решение можно найти в разделе "Утечки" CancellationTokenSource.CancelAfter()? (на самом деле.Net 4.5 реализация конструктора, который вы ищете). Вот функция, которую вы можете использовать для создания токенов тайм-аута на основе этого кода.
public static CancellationTokenSource CreateTimeoutToken(int dueTime) {
if (dueTime < -1) {
throw new ArgumentOutOfRangeException("dueTime");
}
var source = new CancellationTokenSource();
var timer = new Timer(self => {
((Timer)self).Dispose();
try {
source.Cancel();
} catch (ObjectDisposedException) {}
});
timer.Change(dueTime, -1);
return source;
}
FWIW, вы можете использовать async/await в проектах 4.0, просто используйте пакет таргетинга async. Прекрасно работает для меня!
Вы все еще можете использовать CancelAfter()
, который является методом расширения в Microsoft.Bcl.Async
что очень похоже на принятый ответ выше.
Это код источника ссылки, когда я нажимаю F12, чтобы увидеть реализацию CancelAfter()
:
/// <summary>Cancels the <see cref="T:System.Threading.CancellationTokenSource" /> after the specified duration.</summary>
/// <param name="source">The CancellationTokenSource.</param>
/// <param name="dueTime">The due time in milliseconds for the source to be canceled.</param>
public static void CancelAfter(this CancellationTokenSource source, int dueTime)
{
if (source == null)
throw new NullReferenceException();
if (dueTime < -1)
throw new ArgumentOutOfRangeException("dueTime");
Contract.EndContractBlock();
Timer timer = (Timer) null;
timer = new Timer((TimerCallback) (state =>
{
timer.Dispose();
TimerManager.Remove(timer);
try
{
source.Cancel();
}
catch (ObjectDisposedException ex)
{
}
}), (object) null, -1, -1);
TimerManager.Add(timer);
timer.Change(dueTime, -1);
}