超时使用TaskCompletionSource实现的异步方法

我有一个blackbox对象,它暴露了一个异步操作的方法,并在操作完成时触发一个事件。 我已经使用TaskCompletionSource将其包装到Task BlackBoxOperationAysnc()方法中 – 效果很好。

但是,在那个异步包装器中,如果在给定的超时后没有收到事件,我想管理用超时错误完成异步调用。 目前我使用计时器管理它:

 public Task BlackBoxOperationAysnc() { var tcs = new TaskCompletionSource(); const int timeoutMs = 20000; Timer timer = new Timer(_ => tcs.TrySetResult(OpResult.Timeout), null, timeoutMs, Timeout.Infinite); EventHandler eventHandler = (sender, args) => { ... tcs.TrySetResult(OpResult.BlarBlar); } blackBox.EndAsyncOpEvent += eventHandler; blackBox.StartAsyncOp(); return tcs.Task; } 

这是管理超时的唯一方法吗? 有没有设置我自己的计时器 – 我看不到TaskCompletionSource内置的任何超时?

你可以使用CancellationTokenSource超时。 像这样与TaskCompletionSource一起使用它。

例如:

 public Task BlackBoxOperationAysnc() { var tcs = new TaskCompletionSource(); const int timeoutMs = 20000; var ct = new CancellationTokenSource(timeoutMs); ct.Token.Register(() => tcs.TrySetCanceled(), useSynchronizationContext: false); EventHandler eventHandler = (sender, args) => { ... tcs.TrySetResult(OpResult.BlarBlar); } blackBox.EndAsyncOpEvent += eventHandler; blackBox.StartAsyncOp(); return tcs.Task; } 

更新了 ,这是一个完整的function示例:

 using System; using System.ComponentModel; using System.Threading; using System.Threading.Tasks; namespace ConsoleApplication { public class Program { // .NET 4.5/C# 5.0: convert EAP pattern into TAP pattern with timeout public async Task BlackBoxOperationAsync( object state, CancellationToken token, int timeout = Timeout.Infinite) { var tcs = new TaskCompletionSource(); // prepare the timeout CancellationToken newToken; if (timeout != Timeout.Infinite) { var cts = CancellationTokenSource.CreateLinkedTokenSource(token); cts.CancelAfter(timeout); newToken = cts.Token; } else newToken = token; // handle completion AsyncCompletedEventHandler handler = (sender, args) => { if (args.Cancelled) tcs.TrySetCanceled(); else if (args.Error != null) tcs.SetException(args.Error); else tcs.SetResult(args); }; this.BlackBoxOperationCompleted += handler; try { using (newToken.Register(() => tcs.SetCanceled(), useSynchronizationContext: false)) { this.StartBlackBoxOperation(null); return await tcs.Task.ConfigureAwait(continueOnCapturedContext: false); } } finally { this.BlackBoxOperationCompleted -= handler; } } // emulate async operation AsyncCompletedEventHandler BlackBoxOperationCompleted = delegate { }; void StartBlackBoxOperation(object state) { ThreadPool.QueueUserWorkItem(s => { Thread.Sleep(1000); this.BlackBoxOperationCompleted(this, new AsyncCompletedEventArgs(error: null, cancelled: false, userState: state)); }, state); } // test static void Main() { try { new Program().BlackBoxOperationAsync(null, CancellationToken.None, 1200).Wait(); Console.WriteLine("Completed."); new Program().BlackBoxOperationAsync(null, CancellationToken.None, 900).Wait(); } catch (Exception ex) { while (ex is AggregateException) ex = ex.InnerException; Console.WriteLine(ex.Message); } Console.ReadLine(); } } } 

可以在此处找到.NET 4.0 / C#4.0版本,它利用了编译器生成的IEnumerator状态机。