如何“睡眠”,直到在.NET 4.0中请求超时或取消
什么是睡眠一定时间的最佳方式,但能够被CancellationToken
的IsCancellationRequested
中断?
我正在寻找一个在.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是被接受的答案 。