我怎样才能得到与Task在.net 3.5中

本文关键字:net Task | 更新日期: 2023-09-27 17:50:56

我有一些使用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>的情况下如何获得相同的结果?

澄清

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

我怎样才能得到与Task<T>在.net 3.5中

您可以使用NuGet提供的以下包之一:

  • 你可以在NuGet上使用TaskParallelLibrary包。这个包有一个强大的名字,是。net 3.5的。net任务并行库的后端端口,包含在响应扩展中。

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

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

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

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

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的AsyncEnumerator

使用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使用的方法,除非他们有一些令人信服的理由不这样做。

基于@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>()

使用ThreadPool执行方法:

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

Edit for service:

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

编辑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);
    }