Как "спать" до тех пор, пока не будет запрошен тайм-аут или аннулирование в .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);
}
Ответы
Ответ 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);
}
}