簡體   English   中英

任務異步模式和錯誤/異常/取消處理

[英]Task Asynchronous Pattern and error/exception/cancellation handling

我經常有頂級 function 工作的應用程序

public Result5 ProcessAll() {
    var result1 = Process1();
    var result2 = Process2();
    var result3 = Process3(result1);
    var result4 = Process4(result1, result2);
    return Process5(result1, result2, result3, result4);
}

Process* 函數的共同點是:

  • IO 綁定(數據庫、文件系統、網絡服務)
  • 可能會拋出剛剛在調用堆棧中向上傳播的異常
  • 可能會為一些非異常錯誤返回錯誤,這些錯誤應該停止處理並返回

頂級 function 也在可以取消的后台線程上運行。 這意味着完整的實現看起來像

public Result5 ProcessAll(CancellationToken cancellationToken) {
    Result1 result1 = Process1();

    if (result1 == null)
        return null;
    cancellationToken.ThrowIfCancellationRequested();

    Result2 result2 = Process2();

    if (result2 == null)
        return null;
    cancellationToken.ThrowIfCancellationRequested();

    Result3 result3 = Process3(result1);

    if (result3 == null)
        return null;
    cancellationToken.ThrowIfCancellationRequested();

    Result4 result4 = Process4(result1, result2);

    if (result4 == null)
        return null;
    cancellationToken.ThrowIfCancellationRequested();

    return Process5(result1, result2, result3, result4);
}

現在假設我需要通過盡可能多地並行運行來加快速度。

還假設 Process* 函數實現任務異步模式並使用 IO 完成端口或類似端口。

我一直沒能為此找到任何好的模式。
如果我忽略錯誤/異常/取消,它將看起來像這樣。

public Result5 ProcessAll(CancellationToken cancellationToken) {
    Task<Result1> task1 = Process1Async();
    Task<Result2> task2 = Process2Async();

    Task<Result3> task3 = task1.ContinueWith(_ => Process3Async(task1.Result)).Unwrap();

    Task<Result4> task4 = Task.Factory.ContinueWhenAll(new[] { task1, task2 }, 
                                                       _ => Process4Async(task1.Result, task2.Result)).Unwrap();

    // This will trigger all exceptions captured
    Task.WaitAll(new[] { task1, task2, task3, task4 });

    return Process5(task1.Result, task2.Result, task3.Result, task4.Result);
}

(我知道這可以像同步運行 task4 一樣進行優化,並且 WaitAll 不是必需的,但我只是在這里展示一種模式)

如果我現在嘗試處理錯誤和異常,它可能看起來像這樣:

public Result ProcessAll(CancellationToken cancellationToken) {
    Task<Result1> task1 = Process1Async();
    Task<Result2> task2 = Process2Async();

    // Process 3 should not run if task1 or task2 failed or returned error
    Task<Result3> task3 = task1.ContinueWith(_ => {
         if (task1.IsFaulted || task1.Result == null)
             return null;
         if (task2.IsFaulted || (task2.IsCompleted && task2.Result == null)
             return null;
         return Process3Async(task1.Result);
    }).Unwrap();

    // Process4 should not start if any of Process1,Process2 or Process3 returned error or throw exception
    Task<Result4> task4 = Task.Factory.ContinueWhenAll(new[] { task1, task2 }, _ => {
                                                       if (task1.Faulted || task1.Result == null)
                                                           return null;
                                                       if (task2.Faulted || task2.Result == null)
                                                           return null;
                                                       if (task3.Faulted || (task3.IsCompleted && task3.Result == null))
                                                           return null;
                                                       return Process4Async(task1.Result, task2.Result)).Unwrap();

    Task.WaitAll(new[] { task1, task2, task3, task4 });
    if (task1.Result == null || 
        task2.Result == null || 
        task3.Result == null || 
        task4.Result == null)
        return null;
    return Process5(task1.Result, task2.Result, task3.Result, task4.Result);
}

現在我需要進行取消檢查:-)

我現在的問題是:
所有這些對早期任務中的失敗、錯誤和取消的檢查都變得容易出錯並且可擴展性不強。 我在這里錯過了一些重要的東西並且以錯誤的方式做嗎?

我只能在所有較早的進程都完成后才能啟動進程 3、4 和 5,無論如何它們都沒有並行性。 所以你不需要為他們使用任務。 您只需要為前兩個任務使用任務,這樣問題就容易多了。 如果您選擇將它們作為任務啟動,這些任務將始終等待前任,從而消除任何並行性。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM