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);