64

.NET 4.5中有Task.Delay如何让任务在C#4.0中睡眠(或延迟)?

如何在.NET 4.0中执行相同操作?

+2

[Thread.sleep代码(http://msdn.microsoft.com/en-us/library/system.threading.thread.sleep.aspx)? – Default 2013-03-11 15:07:46

+0

看看这里:http://msdn.microsoft.com/en-us/library/hh194845.aspx或者使用Thread.Sleep并添加一个引用来使用System.Threading; – Max 2013-03-11 15:08:22

+0

可能的重复:http://stackoverflow.com/q/4990602/ – Default 2013-03-11 16:57:53

回答

54

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

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; 
} 
+0

谢谢,我没有指定,但我想添加多线程到我的WPF应用程序。我应该在那里使用带回调参数的Timer吗?在'Callback()'定义中使用'Dispatcher.BeginInvoke()'? – Fulproof 2013-03-12 03:40:09

+0

@Fulproof当计时器触发时,你没有执行任何UI交互,所以没有理由。 – Servy 2013-03-12 14:06:06

+1

如何向其中添加cancellationToken,哪个Task.Delay提供? – 2013-07-11 16:29:42

24
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

+3

我将示例添加到您的答案中,以防链接因某种原因死机。 – Default 2013-03-11 15:20:17

+2

@Fulproof他写的是使用LinqPad,它将一个扩展方法添加到'object'中,该方法输出它的'ToString'方法的值。请注意,他没有在其实际实现中使用该方法,也没有使用任何其他非库方法,只是测试它的示例函数。 – Servy 2013-03-11 20:52:24

+0

@Servy,谢谢。我问了这个问题,以减少未知数(并获得答案),但不添加难题来解决。即一个人通常没有专业知识来完成这个难题 – Fulproof 2013-03-12 03:20:31

0

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

+5

这不是一个好主意,CTP包含了永远不会修复的已知错误。使用Bcl.Async是一个更好的选择。 – svick 2013-11-15 12:20:48

60

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

+0

重要的信息是类名是* TaskEx *而不是*任务*! – OneWorld 2017-07-24 12:42:33

6

以下是可取消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; 
     } 
    } 
} 
+0

看起来不太好!如果我用同样的CancellationToken调用很多延迟,我会得到很多代表在代码上注册。 – RollingStone 2017-06-09 13:01:33

1

在很多情况下,一个纯粹的AutoResetEvent比Thread.sleep()方法可能会更好...感兴趣

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

希望它有助于

+0

我认为最初的想法是阻止当前线程,而不是在另一个线程上等待,稍后在当前线程中通知。那么,[this](https://stackoverflow.com/a/46087483/4955344)呢? – 2017-09-07 03:49:49

0
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(); 
     } 
    } 
0

这里有一个简洁的,基于定时器的实现与适当的清理:

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包。

+0

OP要求约4.0 - 4.0中没有“等待”。 – 2017-09-07 03:28:32

+0

'await'关键字是C#语言的一部分,它与.NET Framework版本是分开的。问题在于'任务'没有'await'依赖的'GetAwaiter'。 [Microsoft.Bcl.Async](https://www.nuget.org/packages/Microsoft.Bcl.Async/)提供。我更新了我的答案,提到了NuGet包。 – 2017-09-07 11:20:55

+1

如果一个使用[Microsoft.Bcl.Async](https://www.nuget.org/packages/Microsoft.Bcl.Async/),是不是'TaskEx.Delay'更接受,那么? – 2017-09-08 22:36:54

1

从这个answer扩展思路:

new AutoResetEvent(false).WaitOne(1000);