如何“睡眠”,直到在.NET 4.0中请求超时或取消

什么是睡眠一定时间的最佳方式,但能够被CancellationTokenIsCancellationRequested中断?

我正在寻找一个在.NET 4.0中工作的解决scheme。

我想写

 void MyFunc (CancellationToken ct) { //... // simulate some long lasting operation that should be cancelable Thread.Sleep(TimeSpan.FromMilliseconds(10000), ct); } 

我只是在这里博客:

CancellationToken和Thread.Sleep

简而言之:

 var cancelled = token.WaitHandle.WaitOne(TimeSpan.FromSeconds(5)); 

在你的情况下:

 void MyFunc (CancellationToken ct) { //... // simulate some long lasting operation that should be cancelable var cancelled = ct.WaitHandle.WaitOne(TimeSpan.FromSeconds(10)); } 

或者,我认为这很清楚:

Task.Delay(waitTimeInMs, cancellationToken).Wait();

要在一段时间后取消asynchronous操作,同时仍然可以手动取消操作,请使用类似以下内容的操作

 CancellationTokenSource cts = new CancellationTokenSource(); CancellationToken token = cts.Token; cts.CancelAfter(5000); 

这将导致五秒钟后取消。 要取消操作,您只需将token传递给asynchronous方法,并使用token.ThrowifCancellationRequested()方法,您可以在其中设置一个事件处理函数来激发cts.Cancel()

所以一个完整的例子是:

 CancellationTokenSource cts = new CancellationTokenSource(); CancellationToken token = cts.Token; cts.CancelAfter(5000); // Set up the event handler on some button. if (cancelSource != null) { cancelHandler = delegate { Cancel(cts); }; stopButton.Click -= cancelHandler; stopButton.Click += cancelHandler; } // Now launch the method. SomeMethodAsync(token); 

其中stopButton是您单击以取消正在运行的任务的button

 private void Cancel(CancellationTokenSource cts) { cts.Cancel(); } 

该方法被定义为

 SomeMethodAsync(CancellationToken token) { Task t = Task.Factory.StartNew(() => { msTimeout = 5000; Pump(token); }, token, TaskCreationOptions.None, TaskScheduler.Default); } 

现在,为了使您能够使用线程,并且使用户取消,您需要编写一个“抽取”方法

 int msTimeout; bool timeLimitReached = false; private void Pump(CancellationToken token) { DateTime now = DateTime.Now; System.Timer t = new System.Timer(100); t.Elapsed -= t_Elapsed; t.Elapsed += t_Elapsed; t.Start(); while(!timeLimitReached) { Thread.Sleep(250); token.ThrowIfCancellationRequested(); } } void t_Elapsed(object sender, ElapsedEventArgs e) { TimeSpan elapsed = DateTime.Now - this.readyUpInitialised; if (elapsed > msTimeout) { timeLimitReached = true; t.Stop(); t.Dispose(); } } 

请注意, SomeAsyncMethod将直接返回给调用者。 要阻止呼叫者,也必须在呼叫层次结构中移动Task

到目前为止,我发现的最佳解决scheme是:

 void MyFunc(CancellationToken ct) { //... var timedOut = WaitHandle.WaitAny(new[] { ct.WaitHandle }, TimeSpan.FromMilliseconds(2000)) == WaitHandle.WaitTimeout; var cancelled = ! timedOut; } 

更新:

迄今为止最好的解决scheme是被接受的答案 。