问题描述
我有暴露踢异步操作的方法,以及事件触发时,操作完成一个黑盒子对象。我已经是裹成一个任务<Øpresult>使用TaskCompletionSource BlackBoxOperationAysnc()
法 - 行之有效
I have a blackbox object that exposes a method to kick of an async operation, and an event fires when the operation is complete. I have wrapped that into an Task<OpResult> BlackBoxOperationAysnc()
method using TaskCompletionSource - that works well.
然而,在异步包装我想管理完成与超时错误的异步调用,如果给定的超时后没有收到该事件。目前,我有一个计时器管理它为:
However, in that async wrapper I'd like to manage completing the async call with a timeout error if the event is not received after a given timeout. Currently I manage it with a timer as:
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
Is that the only way to manage a timeout? Is there someway without setting up my own timer - I couldn't see anything timeout built into TaskCompletionSource?
推荐答案
您可以使用一起使用。
You could use CancellationTokenSource with timeout. Use it together with your TaskCompletionSource
like 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;
}
更新,这里有一个完整的功能例如:
Updated, here's a complete functional example:
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, 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 vesion可以发现,它需要编译器生成的<$优势C $ C>的IEnumerator 状态机。
这篇关于暂停时以TaskCompletionSource实现异步方法的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持!