Подтвердить что ты не робот

Как "спать" до тех пор, пока не будет запрошен тайм-аут или аннулирование в .NET 4.0

Какой лучший способ спать определенное количество времени, но быть в состоянии прерваться IsCancellationRequested от CancellationToken?

Я ищу решение, которое работает в .NET 4.0.

Я бы хотел написать

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

Ответ 1

Я просто писал об этом здесь:

ОтменаToken и 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));
}

Ответ 2

В качестве альтернативы, я думаю, это довольно ясно:

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

Ответ 3

Чтобы отменить асинхронную операцию через определенное время, в то время как все еще можно отменить операцию вручную, используйте что-то вроде следующего

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

Это приведет к отмене через пять секунд. Чтобы отменить операцию, все, что вам нужно сделать, это передать token в ваш метод async и использовать метод 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 - кнопка, которую вы нажимаете для отмены выполняемой задачи

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 вверх в иерархии вызовов.

Ответ 4

Лучшее решение, которое я нашел до сих пор:

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

UPDATE:

Лучшим решением пока является принятый ответ.

Ответ 5

CancellationToken.WaitHandle может генерировать исключение при доступе после удаления CancellationTokenSource:

ObjectDisposedException: CancellationTokenSource был удален.

В некоторых случаях, особенно когда связанные источники отмены удаляются вручную (как и должно быть), это может создавать неудобства.

Этот метод расширения позволяет "безопасное ожидание отмены"; однако его следует использовать в сочетании с проверками и надлежащей пометкой состояния токена отмены и/или использования возвращаемого значения. Это связано с тем, что он исключает доступ к 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);
    }
}