我有一个黑盒子对象暴露一个异步操作的方法,当操作完成时触发一个事件.我把它包装成一个任务< OpResult> BlackBoxOperationAysnc()方法使用TaskCompletionSource – 这很好.
但是,在该异步包装器中,如果在给定超时后未收到事件,我想管理异步调用与超时错误.目前我用一个计时器来管理它:
public Task<OpResult> BlackBoxOperationAysnc() { var tcs = new TaskCompletionSource<TestResult>(); const int timeoutMs = 20000; Timer timer = new Timer(_ => tcs.TrySetResult(OpResult.Timeout),null,timeoutMs,Timeout.Infinite); EventHandler<EndOpEventArgs> eventHandler = (sender,args) => { ... tcs.TrySetResult(OpResult.BlarBlar); } blackBox.EndAsyncOpEvent += eventHandler; blackBox.StartAsyncOp(); return tcs.Task; }
这是管理超时的唯一方法吗?有没有设置自己的计时器 – 我看不到任何超时内置在TaskCompletionSource?
解决方法@H_404_10@
您可以使用
CancellationTokenSource超时.与您的TaskCompletionSource一起使用,如
this.
例如.:
public Task<OpResult> BlackBoxOperationAysnc() {
var tcs = new TaskCompletionSource<TestResult>();
const int timeoutMs = 20000;
var ct = new CancellationTokenSource(timeoutMs);
ct.Token.Register(() => tcs.TrySetCanceled(),useSynchronizationContext: false);
EventHandler<EndOpEventArgs> eventHandler = (sender,args) => {
...
tcs.TrySetResult(OpResult.BlarBlar);
}
blackBox.EndAsyncOpEvent += eventHandler;
blackBox.StartAsyncOp();
return tcs.Task;
}
更新了,这里有一个完整的功能示例:
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<AsyncCompletedEventArgs> BlackBoxOperationAsync(
object state,CancellationToken token,int timeout = Timeout.Infinite)
{
var tcs = new TaskCompletionSource<AsyncCompletedEventArgs>();
// 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,900).Wait();
}
catch (Exception ex)
{
while (ex is AggregateException)
ex = ex.InnerException;
Console.WriteLine(ex.Message);
}
Console.ReadLine();
}
}
}
例如.:
public Task<OpResult> BlackBoxOperationAysnc() { var tcs = new TaskCompletionSource<TestResult>(); const int timeoutMs = 20000; var ct = new CancellationTokenSource(timeoutMs); ct.Token.Register(() => tcs.TrySetCanceled(),useSynchronizationContext: false); EventHandler<EndOpEventArgs> eventHandler = (sender,args) => { ... tcs.TrySetResult(OpResult.BlarBlar); } blackBox.EndAsyncOpEvent += eventHandler; blackBox.StartAsyncOp(); return tcs.Task; }
更新了,这里有一个完整的功能示例:
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<AsyncCompletedEventArgs> BlackBoxOperationAsync( object state,CancellationToken token,int timeout = Timeout.Infinite) { var tcs = new TaskCompletionSource<AsyncCompletedEventArgs>(); // 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,900).Wait(); } catch (Exception ex) { while (ex is AggregateException) ex = ex.InnerException; Console.WriteLine(ex.Message); } Console.ReadLine(); } } }