暂停时以TaskCompletionSource实现异步方法 [英] Timeout an async method implemented with TaskCompletionSource

查看:720
本文介绍了暂停时以TaskCompletionSource实现异步方法的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我有暴露踢异步操作的方法,以及事件触发时,操作完成一个黑盒子对象。我已经是裹成一个任务<Ø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?

推荐答案

您可以使用 CancellationTokenSource 与超时。与你的 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实现异步方法的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆