Megjegyzés
Az oldalhoz való hozzáféréshez engedély szükséges. Megpróbálhat bejelentkezni vagy módosítani a címtárat.
Az oldalhoz való hozzáféréshez engedély szükséges. Megpróbálhatja módosítani a címtárat.
Amikor kibocsát egy tevékenységet a TaskCompletionSource<TResult>-ből, Ön lesz a tevékenység élettartamának tulajdonosa. Végezze el ezt a feladatot minden útvonalon. Ha bármelyik folyamat nem fejeződik be, a hívók örökké várakoznak.
Minden kódútvonal befejezése
A feladatot mindig sikeres és sikertelen útvonalakon végezze el. A feladat meghiúsulása esetén használjon blokkot catch a tisztítási logikához. Használjon olyan blokkot finally a törlési logikához, amelyet mindig futtatnia kell. Az alábbi kódblokk egy hibás útvonal tisztításának hozzáadását mutatja be.
public sealed class MissingSetExceptionFix
{
public Task<string> StartAsync(bool fail)
{
var tcs = new TaskCompletionSource<string>(TaskCreationOptions.RunContinuationsAsynchronously);
try
{
if (fail)
{
throw new InvalidOperationException("Simulated failure");
}
tcs.TrySetResult("success");
}
catch (Exception ex)
{
tcs.TrySetException(ex);
}
return tcs.Task;
}
}
Public NotInheritable Class MissingSetExceptionFix
Public Function StartAsync(fail As Boolean) As Task(Of String)
Dim tcs = New TaskCompletionSource(Of String)(TaskCreationOptions.RunContinuationsAsynchronously)
Try
If fail Then
Throw New InvalidOperationException("Simulated failure")
End If
tcs.TrySetResult("success")
Catch ex As Exception
tcs.TrySetException(ex)
End Try
Return tcs.Task
End Function
End Class
Az alábbi kód kivételt kap, naplózza, és elfelejt meghívni SetException vagy TrySetException. Ez a hiba gyakran jelenik meg, és a hívók örökké várakoznak. A tevékenységek kivételkezelésével kapcsolatos további részletekért lásd: Tevékenységkivételek kezelése.
// ⚠️ DON'T copy this snippet. It demonstrates a problem that causes hangs.
public sealed class MissingSetExceptionBug
{
public Task<string> StartAsync(bool fail)
{
var tcs = new TaskCompletionSource<string>(TaskCreationOptions.RunContinuationsAsynchronously);
try
{
if (fail)
{
throw new InvalidOperationException("Simulated failure");
}
tcs.SetResult("success");
}
catch (Exception)
{
// BUG: forgot SetException or TrySetException.
}
return tcs.Task;
}
}
' ⚠️ DON'T copy this snippet. It demonstrates a problem that causes hangs.
Public NotInheritable Class MissingSetExceptionBug
Public Function StartAsync(fail As Boolean) As Task(Of String)
Dim tcs = New TaskCompletionSource(Of String)(TaskCreationOptions.RunContinuationsAsynchronously)
Try
If fail Then
Throw New InvalidOperationException("Simulated failure")
End If
tcs.SetResult("success")
Catch ex As Exception
' BUG: forgot SetException or TrySetException.
End Try
Return tcs.Task
End Function
End Class
Előnyben részesítés TrySet* a befejező versenyeken
Az egyidejű útvonalak gyakran versenyeznek, hogy ugyanazt TaskCompletionSource teljesítsék.
SetResult, SetExceptionmajd SetCanceled dobjon, ha a feladat már befejeződött. A versenyfeltételezett kódban használja a TrySetResult, TrySetException és TrySetCanceled. A konkurens helyzetekben elkerülendő mintákról további információkért lásd: Common async/await bugs.
public static class TrySetRaceExample
{
public static void ShowRaceSafeCompletion()
{
var tcs = new TaskCompletionSource<int>(TaskCreationOptions.RunContinuationsAsynchronously);
bool first = tcs.TrySetResult(42);
bool second = tcs.TrySetException(new TimeoutException("Too late"));
Console.WriteLine($"First completion won: {first}");
Console.WriteLine($"Second completion accepted: {second}");
Console.WriteLine($"Result: {tcs.Task.Result}");
}
}
Public Module TrySetRaceExample
Public Sub ShowRaceSafeCompletion()
Dim tcs = New TaskCompletionSource(Of Integer)(TaskCreationOptions.RunContinuationsAsynchronously)
Dim first As Boolean = tcs.TrySetResult(42)
Dim second As Boolean = tcs.TrySetException(New TimeoutException("Too late"))
Console.WriteLine($"First completion won: {first}")
Console.WriteLine($"Second completion accepted: {second}")
Console.WriteLine($"Result: {tcs.Task.Result}")
End Sub
End Module
Ne vesse el a hivatkozásokat az alaphelyzetbe állítás során
Gyakori hiba jelenik meg a visszaállítható aszinkron primitívekben. Javítsa ki az alaphelyzetbe állítási útvonalat a hivatkozások atomi felcserélésével és az előző feladat elvégzésével (például lemondással):
public sealed class ResetFix
{
private TaskCompletionSource<bool> _signal = NewSignal();
public Task WaitAsync() => _signal.Task;
public void Reset()
{
TaskCompletionSource<bool> previous = Interlocked.Exchange(ref _signal, NewSignal());
previous.TrySetCanceled();
}
public void Pulse()
{
_signal.TrySetResult(true);
}
private static TaskCompletionSource<bool> NewSignal() =>
new(TaskCreationOptions.RunContinuationsAsynchronously);
}
Public NotInheritable Class ResetFix
Private _signal As TaskCompletionSource(Of Boolean) = NewSignal()
Public Function WaitAsync() As Task
Return _signal.Task
End Function
Public Sub Reset()
Dim previous As TaskCompletionSource(Of Boolean) = Interlocked.Exchange(_signal, NewSignal())
previous.TrySetCanceled()
End Sub
Public Sub Pulse()
_signal.TrySetResult(True)
End Sub
Private Shared Function NewSignal() As TaskCompletionSource(Of Boolean)
Return New TaskCompletionSource(Of Boolean)(TaskCreationOptions.RunContinuationsAsynchronously)
End Function
End Class
Ne tegye a következőt: Ha az előző végrehajtása előtt lecserél egy TaskCompletionSource példányt, előfordulhat, hogy a régi feladatot tartalmazó várakozók sosem fejezik be a feladatukat.
// ⚠️ DON'T copy this snippet. It demonstrates a problem where old waiters never complete.
public sealed class ResetBug
{
private TaskCompletionSource<bool> _signal = NewSignal();
public Task WaitAsync() => _signal.Task;
public void Reset()
{
// BUG: waiters on the old task might never complete.
_signal = NewSignal();
}
public void Pulse()
{
_signal.TrySetResult(true);
}
private static TaskCompletionSource<bool> NewSignal() =>
new(TaskCreationOptions.RunContinuationsAsynchronously);
}
' ⚠️ DON'T copy this snippet. It demonstrates a problem where old waiters never complete.
Public NotInheritable Class ResetBug
Private _signal As TaskCompletionSource(Of Boolean) = NewSignal()
Public Function WaitAsync() As Task
Return _signal.Task
End Function
Public Sub Reset()
' BUG: waiters on the old task might never complete.
_signal = NewSignal()
End Sub
Public Sub Pulse()
_signal.TrySetResult(True)
End Sub
Private Shared Function NewSignal() As TaskCompletionSource(Of Boolean)
Return New TaskCompletionSource(Of Boolean)(TaskCreationOptions.RunContinuationsAsynchronously)
End Function
End Class
Checklist
- Végezze el az összes feltárt
TaskCompletionSourcefeladatot a sikeres, sikertelen és megszakítási útvonalakon. - Használjon
TrySet*API-kat olyan útvonalakon, amelyek versenyben lehetnek. - Az alaphelyzetbe állítás során fejezze be vagy szakítsa meg a régi feladatot, mielőtt elveti a referenciát.
- Időtúllépés-alapú tesztek hozzáadása a gyorsabb hibadetektálás érdekében a CI folyamán fellépő lefagyások esetén.