Görevlerinizi tamamlama

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 TaskCompletionSource her 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.

Ayrıca bakınız