【问题标题】:How to put a task to sleep (or delay) in C# 4.0?如何在 C# 4.0 中使任务进入睡眠状态(或延迟)?
【发布时间】:2013-03-11 15:06:49
【问题描述】:

.NET 4.5 中有Task.Delay

如何在 .NET 4.0 中做同样的事情?

【问题讨论】:

  • 看看这里:msdn.microsoft.com/en-us/library/hh194845.aspx 或者使用 Thread.Sleep 并添加对 using System.Threading 的引用;
  • @Default,对于使用 Sleep(),任务应该始终:1)产生一个单独的线程,2)只有一个。不多也不少,3)不能重复使用。这对一项任务毫无意义。这不是骗子,您的链接是关于延迟任务的开始。我的问题是在它启动后的任何时候让它进入睡眠状态
  • 在代码中使用Thread.Sleep 几乎总是一个错误。

标签: c# .net multithreading parallel-processing task-parallel-library


【解决方案1】:

使用来自 NuGet 的 Microsoft.Bcl.Async 包,它有 TaskEx.Delay

【讨论】:

  • 重要的信息是类名是 TaskEx 而不是 Task
  • 这应该是答案。
【解决方案2】:

您可以在 4.0 中使用Timer 创建Delay 方法:

public static Task Delay(double milliseconds)
{
    var tcs = new TaskCompletionSource<bool>();
    System.Timers.Timer timer = new System.Timers.Timer();
    timer.Elapsed+=(obj, args) =>
    {
        tcs.TrySetResult(true);
    };
    timer.Interval = milliseconds;
    timer.AutoReset = false;
    timer.Start();
    return tcs.Task;
}

【讨论】:

  • 谢谢,我没有指定,但我正在尝试将多线程添加到我的 WPF 应用程序中。我应该在那里使用带有回调参数的计时器吗?在Callback() 定义中使用Dispatcher.BeginInvoke()?
  • @Fulproof 计时器触发时您没有执行任何 UI 交互,因此没有理由这样做。
  • 如何添加cancellationToken,Task.Delay提供的?
  • 这不会造成竞争条件吗?如果计时器对象碰巧在计时器到期之前被垃圾回收,那么TrySetResult 似乎永远不会被调用。
  • @EdwardBrey Timer 类在内部专门处理这个问题,以确保它的用户在它的生命周期内不需要保留对它的引用。只要计时器当前正在运行,它就会从根位置添加对自身的引用,然后在不再运行时将其删除。
【解决方案3】:
using System;
using System.Threading;
using System.Threading.Tasks;

class Program
{
    static void Main()
    {
        Delay(2000).ContinueWith(_ => Console.WriteLine("Done"));
        Console.Read();
    }

    static Task Delay(int milliseconds)
    {
        var tcs = new TaskCompletionSource<object>();
        new Timer(_ => tcs.SetResult(null)).Change(milliseconds, -1);
        return tcs.Task;
    }
}

来自How to implement Task.Delay in 4.0部分

【讨论】:

  • 我将示例添加到您的答案中,以防链接因某种原因失效。
  • @Fulproof 他写的是使用LinqPad,它为object 添加了一个扩展方法,将ToString 方法的值打印出来。请注意,他在实际实现中没有使用它,也没有使用任何其他非库方法,只是测试它的示例函数。
  • @Servy,谢谢。我问这个问题是为了减少未知数(并获得答案),而不是添加谜题来解决。 IE。提问的人通常不具备完成谜题的专业知识
  • 已更新,您只需复制粘贴即可运行)
  • 不应该丢弃定时器吗?
【解决方案4】:

以下是可取消的 Task.Delay 实现的代码和示例工具。您可能对Delay 方法感兴趣。:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace DelayImplementation
{
    class Program
    {
        static void Main(string[] args)
        {
            System.Threading.CancellationTokenSource tcs = new System.Threading.CancellationTokenSource();

            int id = 1;
            Console.WriteLine(string.Format("Starting new delay task {0}. This one will be cancelled.", id));
            Task delayTask = Delay(8000, tcs.Token);
            HandleTask(delayTask, id);

            System.Threading.Thread.Sleep(2000);
            tcs.Cancel();

            id = 2;
            System.Threading.CancellationTokenSource tcs2 = new System.Threading.CancellationTokenSource();
            Console.WriteLine(string.Format("Starting delay task {0}. This one will NOT be cancelled.", id));
            var delayTask2 = Delay(4000, tcs2.Token);
            HandleTask(delayTask2, id);

            System.Console.ReadLine();
        }

        private static void HandleTask(Task delayTask, int id)
        {
            delayTask.ContinueWith(p => Console.WriteLine(string.Format("Task {0} was cancelled.", id)), TaskContinuationOptions.OnlyOnCanceled);
            delayTask.ContinueWith(p => Console.WriteLine(string.Format("Task {0} was completed.", id)), TaskContinuationOptions.OnlyOnRanToCompletion);
        }

        static Task Delay(int delayTime, System.Threading.CancellationToken token)
        {
            TaskCompletionSource<object> tcs = new TaskCompletionSource<object>();

            if (delayTime < 0) throw new ArgumentOutOfRangeException("Delay time cannot be under 0");

            System.Threading.Timer timer = null;
            timer = new System.Threading.Timer(p =>
            {
                timer.Dispose(); //stop the timer
                tcs.TrySetResult(null); //timer expired, attempt to move task to the completed state.
            }, null, delayTime, System.Threading.Timeout.Infinite);

            token.Register(() =>
                {
                    timer.Dispose(); //stop the timer
                    tcs.TrySetCanceled(); //attempt to mode task to canceled state
                });

            return tcs.Task;
        }
    }
}

【讨论】:

  • 看起来不太好!如果我使用相同的 CancellationToken 调用很多延迟,我会在令牌上注册很多代表。
【解决方案5】:

从这个answer扩展这个想法:

new AutoResetEvent(false).WaitOne(1000);

【讨论】:

    【解决方案6】:

    您可以下载Visual Studio Async CTP 并使用TaskEx.Delay

    【讨论】:

    • 这不是一个好主意,CTP 包含永远无法修复的已知错误。使用 Bcl.Async 是更好的选择。
    【解决方案7】:

    在许多情况下,纯粹的 AutoResetEvent 比 Thread.Sleep()...

    AutoResetEvent pause = new AutoResetEvent(false);
    Task timeout = Task.Factory.StartNew(()=>{
    pause.WaitOne(1000, true);
    });
    

    希望对你有帮助

    【讨论】:

    • 我认为最初的想法是阻塞当前线程,而不是在另一个线程上等待,然后在当前线程上得到通知。那么,this 怎么样?
    【解决方案8】:
        public static void DelayExecute(double delay, Action actionToExecute)
        {
            if (actionToExecute != null)
            {
                var timer = new DispatcherTimer
                {
                    Interval = TimeSpan.FromMilliseconds(delay)
                };
                timer.Tick += (delegate
                {
                    timer.Stop();
                    actionToExecute();
                });
                timer.Start();
            }
        }
    

    【讨论】:

      【解决方案9】:

      这是一个简洁的、基于计时器的实现,并进行了适当的清理:

      var wait = new TaskCompletionSource<bool>();
      using (new Timer(_ => wait.SetResult(false), null, delay, Timeout.Infinite))
          await wait.Task;
      

      要在 .NET 4.0 上使用此代码,您需要 Microsoft.Bcl.Async NuGet 包。

      【讨论】:

      • OP 在询问 4.0 - 4.0 中没有“等待”。
      • await 关键字是 C# 语言的一部分,与 .NET Framework 版本不同。问题是Task&lt;T&gt; 没有GetAwaiterawait 依赖它。 Microsoft.Bcl.Async 提供这个。我更新了我的答案以提及 NuGet 包。
      • 如果使用Microsoft.Bcl.Async,那TaskEx.Delay不是更简洁吗?
      猜你喜欢
      • 1970-01-01
      • 2012-06-21
      • 2013-03-03
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2012-04-02
      相关资源
      最近更新 更多