Notitie
Voor toegang tot deze pagina is autorisatie vereist. U kunt proberen u aan te melden of de directory te wijzigen.
Voor toegang tot deze pagina is autorisatie vereist. U kunt proberen de mappen te wijzigen.
Soms hebt u annulering nodig, maar de bewerking waarop u wacht, accepteert geen CancellationToken. Kies in dat geval het gedrag dat u nodig hebt:
- De bewerking zelf annuleren.
- Annuleer alleen uw wachttijd.
- Annuleer zowel de bewerking als de wachttijd.
De juiste keuze is afhankelijk van wie eigenaar is van de bewerking en welke opschoongaranties u nodig hebt.
De drie annulerings betekenissen begrijpen
Als mensen zeggen: 'Deze asynchrone aanroep annuleren', betekenen ze meestal een van de drie verschillende dingen:
- De bewerking annuleren. Geef het signaal aan om de werkzaamheden te stoppen.
- Annuleer de wachttijd. Wacht niet meer en ga verder met uw werkstroom, zelfs als de bewerking nog steeds wordt uitgevoerd.
- Annuleer beide. De aanvraagbewerking annuleren en ook onmiddellijk stoppen met wachten.
Behandel deze betekenissen als afzonderlijke ontwerpbeslissingen. Als u ze mengt, wordt het annuleringsgedrag moeilijk te begrijpen.
Liever tokenbewuste API's indien beschikbaar
Controleer voordat u een wrapper toevoegt of de API al annuleringstokens ondersteunt. Moderne .NET API's hebben veel meer ondersteuning voor tokens dan oudere .NET Framework-code. Veel stream-API's in .NET bieden nu bijvoorbeeld ondersteuning voor annulering en NetworkStream asynchrone bewerkingen volgen annuleringstokens.
Gebruik tokenoverbelastingen wanneer ze bestaan:
public static class StreamExamples
{
public static async Task<int> ReadOnceAsync(
NetworkStream stream,
byte[] buffer,
CancellationToken cancellationToken)
{
return await stream.ReadAsync(
buffer.AsMemory(0, buffer.Length),
cancellationToken);
}
}
Public Module StreamExamples
Public Async Function ReadOnceAsync(
stream As NetworkStream,
buffer As Byte(),
cancellationToken As CancellationToken) As Task(Of Integer)
Return Await stream.ReadAsync(
buffer.AsMemory(0, buffer.Length),
cancellationToken)
End Function
End Module
Alleen de wachttijd annuleren met behulp van Task.WhenAny
Wanneer een bewerking geen token accepteert, kunt u uw wachttijd annuleren door de bewerking parallel aan een taak die een token gebruikt uit te voeren. Dit patroon komt vaak voor als WithCancellation-helper.
public static class TaskCancellationExtensions
{
public static async Task<T> WithCancellation<T>(
this Task<T> task,
CancellationToken cancellationToken)
{
if (task.IsCompleted)
return await task.ConfigureAwait(false);
var cancellationTaskSource = new TaskCompletionSource<bool>(
TaskCreationOptions.RunContinuationsAsynchronously);
using var registration = cancellationToken.Register(
static state =>
((TaskCompletionSource<bool>)state!).TrySetResult(true),
cancellationTaskSource);
Task completed = await Task.WhenAny(task, cancellationTaskSource.Task)
.ConfigureAwait(false);
if (completed != task)
throw new OperationCanceledException(cancellationToken);
return await task.ConfigureAwait(false);
}
}
Public Module TaskCancellationExtensions
<Extension()>
Public Async Function WithCancellation(Of T)(
operationTask As Task(Of T),
cancellationToken As CancellationToken) As Task(Of T)
If operationTask.IsCompleted Then
Return Await operationTask
End If
Dim cancellationTaskSource =
New TaskCompletionSource(Of Boolean)(TaskCreationOptions.RunContinuationsAsynchronously)
Using registration = cancellationToken.Register(
Sub(state)
DirectCast(state, TaskCompletionSource(Of Boolean)).TrySetResult(True)
End Sub,
cancellationTaskSource)
Dim completed = Await Task.WhenAny(operationTask, cancellationTaskSource.Task)
If completed IsNot operationTask Then
Throw New OperationCanceledException(cancellationToken)
End If
End Using
Return Await operationTask
End Function
End Module
Dit patroon gebruikt WhenAny om te retourneren zodra een van beide taken is voltooid.
Gebruik deze benadering alleen wanneer het veilig is voor de oorspronkelijke bewerking om op de achtergrond door te gaan.
Beide bewerkingen annuleren en wachten wanneer u de eigenaar van de bewerking bent
Als u de eigenaar van de bewerking bent en een token accepteert, geeft u het token door en gebruikt u nog steeds een annuleerbare wachttijd wanneer dat nodig is:
public static class CancelBothDemo
{
public static async Task<string> FetchDataAsync(CancellationToken cancellationToken)
{
await Task.Delay(500, cancellationToken);
return "payload";
}
public static async Task RunAsync()
{
using var cts = new CancellationTokenSource();
cts.CancelAfter(100);
try
{
string payload = await FetchDataAsync(cts.Token)
.WithCancellation(cts.Token);
Console.WriteLine(payload);
}
catch (OperationCanceledException)
{
Console.WriteLine("Canceled operation and wait.");
}
}
}
Public Module CancelBothDemo
Public Async Function FetchDataAsync(cancellationToken As CancellationToken) As Task(Of String)
Await Task.Delay(500, cancellationToken)
Return "payload"
End Function
Public Async Function RunAsync() As Task
Using cts = New CancellationTokenSource()
cts.CancelAfter(100)
Try
Dim payload = Await FetchDataAsync(cts.Token).WithCancellation(cts.Token)
Console.WriteLine(payload)
Catch ex As OperationCanceledException
Console.WriteLine("Canceled operation and wait.")
End Try
End Using
End Function
End Module
Deze combinatie geeft responsieve annulering voor bellers en coƶperatief afsluiten voor het onderliggende werk.
Afgeslagen bewerkingen veilig afhandelen
Als u alleen de wachttijd annuleert, kan de oorspronkelijke taak later een fout opleveren. Behoud een referentie zodat u de voltooiing kunt observeren en uitzonderingen kunt loggen. Anders kunt u fouten missen en het oplossen van problemen moeilijker maken.
public static class ObserveLateFaultDemo
{
private static async Task<int> FaultLaterAsync()
{
await Task.Delay(250);
throw new InvalidOperationException("Background operation failed.");
}
public static async Task RunAsync()
{
Task<int> operation = FaultLaterAsync();
using var cts = new CancellationTokenSource(50);
try
{
await operation.WithCancellation(cts.Token);
}
catch (OperationCanceledException)
{
Console.WriteLine("Stopped waiting; operation still running.");
}
_ = operation.ContinueWith(
t => Console.WriteLine($"Observed late fault: {t.Exception!.InnerException!.Message}"),
TaskContinuationOptions.OnlyOnFaulted);
await Task.Delay(300);
}
}
Public Module ObserveLateFaultDemo
Private Async Function FaultLaterAsync() As Task(Of Integer)
Await Task.Delay(250)
Throw New InvalidOperationException("Background operation failed.")
End Function
Public Async Function RunAsync() As Task
Dim operation As Task(Of Integer) = FaultLaterAsync()
Using cts = New CancellationTokenSource(50)
Try
Await operation.WithCancellation(cts.Token)
Catch ex As OperationCanceledException
Console.WriteLine("Stopped waiting; operation still running.")
End Try
End Using
Dim observed = operation.ContinueWith(
Sub(t)
Console.WriteLine($"Observed late fault: {t.Exception.InnerException.Message}")
End Sub,
TaskContinuationOptions.OnlyOnFaulted)
Await observed
End Function
End Module