如何在.net 3.5中获得等效的Task <t> </t>

时间:2014-03-19 19:32:43

标签: c# asynchronous .net-3.5 task-parallel-library task

我有一些使用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();
}

此代码背后的想法是在指定的时间间隔内没有新角色到达时返回结果。

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

如何在不使用Task<T>的情况下获得相同的结果?

澄清

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

5 个答案:

答案 0 :(得分:18)

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

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

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

答案 1 :(得分:4)

  

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

使用C#2.0 - 4.0编写此类方案的显着且方便的方法是使用自驱动IEnumerableyield。它允许实现异步状态机,类似于C#5.0的async/await。这样,您可以为异步逻辑保留方便的线性代码流。所有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();
        }
    }
}

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

有很多实现利用这种方法,例如Jeffrey Richter's AsyncEnumerator

答案 2 :(得分:3)

使用Timer(实际上是Delay内部实施的方式)。

private static HashSet<Timer> timers = new HashSet<Timer>();
public static void ExecuteAfter(Action action, TimeSpan delay)
{
    Timer timer = null;
    timer = new System.Threading.Timer(s =>
    {
        action();
        timer.Dispose();
        lock (timers)
            timers.Remove(timer);
    }, null, (long)delay.TotalMilliseconds, Timeout.Infinite);
    lock (timers)
        timers.Add(timer);
}

对于编辑,如果您正在使用基于异步IO构建的异步应用程序,那么异步IO将已经暴露了一些异步方法。它可以是基于事件的模型,它可以接受回调,它可以使用IAsyncResult等。Task是另一种可能的异步编程方法,你当然能够翻译任何其他方法的任何方法,如果一个方法比你更好,但通常人们倾向于坚持使用他们正在执行的基础IO的任何方法,除非他们有一些令人信服的理由不这样做。

答案 3 :(得分:0)

根据@Servy的回答,对我的工作做了一些修改:

ExecuteAfter(() => MessageBox.Show("hi"), 1000 ); 

代码:

public static void ExecuteAfter(Action action, int milliseconds)
{
    System.Threading.Timer timer = null;
    timer = new System.Threading.Timer(s =>
    {
        action();
        timer.Dispose();
        lock (timers)
            timers.Remove(timer);
    }, null, milliseconds, UInt32.MaxValue-10);
    lock (timers)
        timers.Add(timer);
}

private static HashSet<System.Threading.Timer> timers = new HashSet<System.Threading.Timer>()

答案 4 :(得分:-2)

使用ThreadPool执行方法:

http://msdn.microsoft.com/en-us/library/kbf0f1ct(v=vs.110).aspx

为Servy编辑:

任务并行库和PLINQ的默认调度程序使用.NET Framework ThreadPool来排队和执行工作。在.NET Framework 4中,ThreadPool使用System.Threading.Tasks.Task类型提供的信息来有效地支持并行任务和查询经常表示的细粒度并行性(短期工作单元)。 / p>

编辑2(由于负面反馈):

此代码完全符合OP的要求:

    void ReturnResponseAfterAShortDelay(WaitHandle cancellationToken)
    {
        log.InfoFormat("Deferring response for {0} ms", Settings.Default.TimeoutMs);
        ThreadPool.RegisterWaitForSingleObject(cancellationToken, 
            () => 
            {
                if (!cancellationToken.WaitOne(0))
                {
                    ReturnWhateverHasArrived();
                }
            }, 
            null, Settings.Default.TimeoutMs, true);
    }