C# 如何“休眠”直到在 .NET 4.0 中请求超时或取消

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/18715099/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-10 12:59:24  来源:igfitidea点击:

How to "sleep" until timeout or cancellation is requested in .NET 4.0

c#.net-4.0sleepcancellationcancellationtokensource

提问by Onur

What's the best way to sleep a certain amount of time, but be able to be interrupted by a IsCancellationRequestedfrom a CancellationToken?

睡眠一定时间的最佳方式是什么,但能够被 a IsCancellationRequestedfrom a打断CancellationToken

I'm looking for a solution which works in .NET 4.0.

我正在寻找适用于 .NET 4.0 的解决方案。

I'd like to write

我想写

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

采纳答案by Frode

I just blogged about it here:

我刚刚在这里写了一篇博客:

CancellationToken and Thread.Sleep

CancellationToken 和 Thread.Sleep

in Short:

简而言之:

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

In your context:

在您的上下文中:

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

回答by Onur

The best solution I found so far is:

到目前为止,我找到的最佳解决方案是:

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

UPDATE:

更新:

The best solution so far is the accepted answer.

迄今为止最好的解决方案是接受的答案

回答by MoonKnight

To cancel an asynchronious operation after a certain amount of time whilst still being able to cancel the operation manually use something like the following

要在一定时间后取消异步操作同时仍然能够手动取消操作,请使用以下内容

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

This will cause a cancellation after five seconds. To cancel the operation your self all you have to do is pass the tokeninto your async method and use the token.ThrowifCancellationRequested()method, where you have set up an event handler somewhere to fire cts.Cancel().

这将导致五秒后取消。要取消您自己的操作,您所要做的就是将 传递给token您的异步方法并使用该token.ThrowifCancellationRequested()方法,您已经在某处设置了一个事件处理程序来触发cts.Cancel()

So a full example is:

所以一个完整的例子是:

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

Where stopButtonis the button you click to cancel the running task

stopButton你点击取消正在运行的任务的按钮在哪里

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

and the method is defined as

该方法定义为

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

Now, to enable you to work the thread but also enable user cancellation, you will need to write a 'pumping' method

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

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

Note, SomeAsyncMethodwill return right to the caller. To block the caller aswell you will have to move the Taskup in the call hierarchy.

注意,SomeAsyncMethod将正确返回给调用者。要阻止呼叫者,您必须Task在呼叫层次结构中向上移动。

回答by Fowl

Alternatively, I think this is pretty clear:

或者,我认为这很清楚:

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

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

回答by user2864740

The CancellationToken.WaitHandlecan throw an exception when accessed after the CancellationTokenSource has been disposed:

CancellationToken.WaitHandle可以抛出当CancellationTokenSource后访问异常已被处置:

ObjectDisposedException: The CancellationTokenSource has been disposed.

ObjectDisposedException: CancellationTokenSource 已被处理。

In some cases, especially when linked cancellation sourcesare being manually disposed (as they should be), this can be a nuisance.

在某些情况下,尤其是当手动处理链接的取消源时(应该如此),这可能会令人讨厌。

This extension method allows 'safe cancellation waiting'; however, it should be used on conjunction with checks to, and proper flagging of, the cancellation token's state and/or usage of the return value. This is because it suppresses exceptions access the WaitHandle and may return faster than expected.

此扩展方法允许“安全取消等待”;但是,它应该与对取消令牌的状态和/或返回值的使用的检查和正确标记结合使用。这是因为它会抑制异常访问 WaitHandle,并且返回速度可能比预期的要快。

internal static class CancellationTokenExtensions
{
    /// <summary>
    /// Wait up to a given duration for a token to be cancelled.
    /// Returns true if the token was cancelled within the duration
    /// or the underlying cancellation token source has been disposed.
    /// </summary>
    public static bool WaitForCancellation(this CancellationToken token, TimeSpan duration)
    {
        WaitHandle handle;
        try
        {
            handle = token.WaitHandle;
        }
        catch
        {
            // eg. CancellationTokenSource is disposed
            return true;
        }

        return handle.WaitOne(duration);
    }
}