如何在.net 3.5中获得等效的Task <T>?

Tim*_*ong 10 c# asynchronous task .net-3.5 task-parallel-library

我有一些使用的代码Task<T>推迟从串行读取操作返回结果很短的时间,如下所示:

void ReturnResponseAfterAShortDelay()
{
    if (delayedResponseCancellationTokenSource != null)
        delayedResponseCancellationTokenSource.Cancel(); // Cancel any pending operations and start a new one.

   delayedResponseCancellationTokenSource = new CancellationTokenSource();

   log.InfoFormat("Deferring response for {0} ms", Settings.Default.TimeoutMs);

   Task.Delay(Properties.Settings.Default.TimeoutMs, delayedResponseCancellationTokenSource.Token)
       .ContinueWith((continuation) => ReturnWhateverHasArrived(), TaskContinuationOptions.NotOnCanceled)
       .Start();
}
Run Code Online (Sandbox Code Playgroud)

此代码背后的想法是在没有新字符到达指定间隔时返回结果.

但是,由于我无法控制的因素,我必须使用.NET 3.5,这会阻止我使用Task<T>,因此我必须以某种方式重构此代码.

如何在不使用的情况下实现相同的结果Task<T>

澄清

虽然我展示的具体代码恰好是一个定时延迟,但我的用法并不仅限于延迟.可能还有其他情况我想立即开始一些"长时间运行"的轮询任务.典型的情况是I/O绑定操作,例如,定期查询连接到串行端口的设备,然后在满足某些条件时引发事件.

Sam*_*ell 18

您可以使用NuGet提供的以下任一软件包:

  • 您可以在NuGet上使用TaskParallelLibrary包.此程序包具有强大的名称,是.NET 4的.NET任务并行库的.NET 3.5后端程序,包含在Reactive Extensions中.

  • 您可以在NuGet上使用System.Threading.Tasks.Unofficial包.此替代实现使用Mono代码库而不是Microsoft实现.请注意,此程序包中包含的程序集没有强名称,因此如果您的库使用强名称,则此选项不可用.


nos*_*tio 4

虽然我展示的具体代码恰好是定时延迟,但我的用法并不限于延迟事物。可能还有其他情况,我想立即启动一些“长时间运行”的轮询任务。典型的情况是 I/O 绑定操作,例如定期查询连接到串行端口的设备,然后在满足某些条件时引发事件。

使用 C# 2.0 - 4.0 编码此类场景的一个值得注意且方便的技巧是使用自驱动IEnumerableyield. 它允许实现异步状态机,类似于async/awaitC# 5.0。这样,您就可以为异步逻辑保留方便的线性代码流。所有 C# 语言代码控制语句都可以工作(除了不能yield return从内部执行try/catch)。

例如,带有计时器的控制台应用程序:

using System;
using System.Collections;
using System.Threading;

namespace ConsoleApplication_22516303
{
    class Program
    {
        class AsyncLogic
        {
            public EventHandler Completed = delegate { };

            IEnumerable WorkAsync(Action nextStep)
            {
                using (var timer = new System.Threading.Timer(_ => nextStep()))
                {
                    timer.Change(0, 500);

                    var tick = 0;
                    while (tick < 10)
                    {
                        // resume upon next timer tick
                        yield return Type.Missing;
                        Console.WriteLine("Tick: " + tick++);
                    }
                }

                this.Completed(this, EventArgs.Empty);
            }

            public void Start()
            {
                IEnumerator enumerator = null;
                Action nextStep = () => enumerator.MoveNext();
                enumerator = WorkAsync(nextStep).GetEnumerator();
                nextStep();
            }
        }

        static void Main(string[] args)
        {
            var mre = new ManualResetEvent(false);
            var asyncLogic = new AsyncLogic();
            asyncLogic.Completed += (s, e) => mre.Set();
            asyncLogic.Start();
            mre.WaitOne();
            Console.WriteLine("Completed, press Enter to exit");
            Console.ReadLine();
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

任何事件都可以用一个处理程序来包装,该处理程序将调用nextStep,类似于上面的计时器回调。yield return该代码将在相应的, 事件发生后继续。

有相当多的实现利用了这种方法,例如Jeffrey Richter 的AsyncEnumerator.