Not
Bu sayfaya erişim yetkilendirme gerektiriyor. Oturum açmayı veya dizinleri değiştirmeyi deneyebilirsiniz.
Bu sayfaya erişim yetkilendirme gerektiriyor. Dizinleri değiştirmeyi deneyebilirsiniz.
TaskCompletionSource<TResult> uygulamasıyla bir görevi erişime açtığınızda, görevin yaşam süresi sizin kontrolünüzdedir. Bu görevi her yolda tamamlayın. Herhangi bir yol tamamlanmayı atlarsa, arayanlar sonsuza kadar bekler.
Her kod yolunu tamamlama
Görevi her zaman başarı ve başarısızlık yollarında tamamlayın. Görev başarısız olduğunda temizleme mantığı için bir catch blok kullanın. Temizleme mantığı için her zaman çalışması gereken bir finally blok kullanın. Aşağıdaki kod bloğu, hata yolu için temizleme eklemeyi gösterir:
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
Aşağıdaki kod bir özel durum yakalar, bunu günlüğe kaydeder ve SetException veya TrySetException çağrısını yapmayı unutur. Bu hata sık sık görünür ve arayanların sonsuza kadar beklemesine neden olur. Görevlerle özel durum işleme hakkında daha fazla ayrıntı için Görev özel durum işleme bölümüne bkz.
// ⚠️ 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
Tamamlanma yarışlarında TrySet* tercih et
Eşzamanlı yollar genellikle aynı TaskCompletionSourceişlemi tamamlamak için yarışıyor.
SetResult, SetExceptionve SetCanceled görev zaten tamamlandıysa at. Yarışa açık kodda TrySetResult, TrySetException ve TrySetCanceled kullanın. Eşzamanlı senaryolarda kaçınılması gereken diğer desenler için bkz. Ortak async/await hataları.
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
Sıfırlama sırasında referansları bırakmayın
Sıfırlanabilir asenkron primitiflerde yaygın bir hata görünür. Referansları atomik olarak değiştirerek ve önceki görevi (örneğin iptal ile) tamamlayarak sıfırlama yolunu düzeltin.
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
Bunu yapmayın: Öncekini tamamlamadan önce bir TaskCompletionSource örneği değiştirirseniz, eski görevi tutan garsonlar hiçbir zaman tamamlanmayabilir.
// ⚠️ 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
- Başarı, başarısızlık ve iptal yollarında açığa çıkan
TaskCompletionSourceher görevi tamamlayın. - Yarışabilecek yollarda API'leri kullanın
TrySet*. - Sıfırlama sırasında, referansını bırakmadan önce eski görevi tamamlayın veya iptal edin.
- CI'de beklemelerin hızla başarısız olması için zaman aşımına dayalı testler ekleyin.