Ben bağlandığı bir bir iş parçacığı güvenli sınıf yapılan CancellationTokenSource
bir etmek Task
ve garanti o CancellationTokenSource
zaman onun ilişkili bertaraf edilecektir Task
tamamlanana. CancellationTokenSource
Bertarafı sırasında veya sonrasında iptal edilmemesini sağlamak için kilitler kullanır . Bu, belgelere uymak için gerçekleşir , şunları belirtir:
Dispose
Tüm diğer işlemler sırasında yöntemi yalnızca kullanılan olmalıdır CancellationTokenSource
nesne tamamladık.
Ve ayrıca :
Dispose
Yöntem yaprak CancellationTokenSource
kullanılamaz bir halde bulunur.
İşte sınıf:
public class CancelableExecution
{
private readonly bool _allowConcurrency;
private Operation _activeOperation;
private class Operation : IDisposable
{
private readonly object _locker = new object();
private readonly CancellationTokenSource _cts;
private readonly TaskCompletionSource<bool> _completionSource;
private bool _disposed;
public Task Completion => _completionSource.Task; // Never fails
public Operation(CancellationTokenSource cts)
{
_cts = cts;
_completionSource = new TaskCompletionSource<bool>(
TaskCreationOptions.RunContinuationsAsynchronously);
}
public void Cancel()
{
lock (_locker) if (!_disposed) _cts.Cancel();
}
void IDisposable.Dispose() // Is called only once
{
try
{
lock (_locker) { _cts.Dispose(); _disposed = true; }
}
finally { _completionSource.SetResult(true); }
}
}
public CancelableExecution(bool allowConcurrency)
{
_allowConcurrency = allowConcurrency;
}
public CancelableExecution() : this(false) { }
public bool IsRunning =>
Interlocked.CompareExchange(ref _activeOperation, null, null) != null;
public async Task<TResult> RunAsync<TResult>(
Func<CancellationToken, Task<TResult>> taskFactory,
CancellationToken extraToken = default)
{
var cts = CancellationTokenSource.CreateLinkedTokenSource(extraToken, default);
using (var operation = new Operation(cts))
{
// Set this as the active operation
var oldOperation = Interlocked.Exchange(ref _activeOperation, operation);
try
{
if (oldOperation != null && !_allowConcurrency)
{
oldOperation.Cancel();
await oldOperation.Completion; // Continue on captured context
}
var task = taskFactory(cts.Token); // Run in the initial context
return await task.ConfigureAwait(false);
}
finally
{
// If this is still the active operation, set it back to null
Interlocked.CompareExchange(ref _activeOperation, null, operation);
}
}
}
public Task RunAsync(Func<CancellationToken, Task> taskFactory,
CancellationToken extraToken = default)
{
return RunAsync<object>(async ct =>
{
await taskFactory(ct).ConfigureAwait(false);
return null;
}, extraToken);
}
public Task CancelAsync()
{
var operation = Interlocked.CompareExchange(ref _activeOperation, null, null);
if (operation == null) return Task.CompletedTask;
operation.Cancel();
return operation.Completion;
}
public bool Cancel() => CancelAsync() != Task.CompletedTask;
}
CancelableExecution
Sınıfın birincil yöntemleri RunAsync
ve Cancel
. Varsayılan olarak, eşzamanlı işlemlere izin verilmez; bu RunAsync
, yeni bir işleme başlamadan önce ikinci kez çağırmanın sessizce iptal edeceğini ve önceki işlemin (hala çalışıyorsa) tamamlanmasını bekleyeceğini gösterir.
Bu sınıf her türlü uygulamada kullanılabilir. Birincil kullanımı, UI uygulamalarında, eşzamansız bir işlemi başlatmak ve iptal etmek için düğmelere sahip formların içinde veya seçilen öğe her değiştiğinde bir işlemi iptal eden ve yeniden başlatan bir liste kutusuyla. İlk durumun bir örneği:
private readonly CancelableExecution _cancelableExecution = new CancelableExecution();
private async void btnExecute_Click(object sender, EventArgs e)
{
string result;
try
{
Cursor = Cursors.WaitCursor;
btnExecute.Enabled = false;
btnCancel.Enabled = true;
result = await _cancelableExecution.RunAsync(async ct =>
{
await Task.Delay(3000, ct); // Simulate some cancelable I/O operation
return "Hello!";
});
}
catch (OperationCanceledException)
{
return;
}
finally
{
btnExecute.Enabled = true;
btnCancel.Enabled = false;
Cursor = Cursors.Default;
}
this.Text += result;
}
private void btnCancel_Click(object sender, EventArgs e)
{
_cancelableExecution.Cancel();
}
RunAsync
Yöntemi bir ekstra kabul CancellationToken
dahili olarak oluşturulan bağlıdır argüman olarak CancellationTokenSource
. Bu isteğe bağlı belirtecin sağlanması, ilerleyen senaryolarda yararlı olabilir.