web-dev-qa-db-ja.com

Task.WhenAllで例外をスローするタスクを無視し、完了した結果のみを取得します

例外をスローする場合としない場合があるタスクが多数あるタスク並列問題に取り組んでいます。

正常に終了したすべてのタスクを処理し、残りをログに記録したいと思います。 _Task.WhenAll_は、残りの結果を収集することを許可せずに、タスク例外をプロポーションします。

_    static readonly Task<string> NormalTask1 = Task.FromResult("Task result 1");
    static readonly Task<string> NormalTask2 = Task.FromResult("Task result 2");
    static readonly Task<string> ExceptionTk = Task.FromException<string>(new Exception("Bad Task"));
    var results = await Task.WhenAll(new []{ NormalTask1,NormalTask2,ExceptionTk});
_

_Task.WhenAll_は、残りの結果を無視してExcceptionTkの例外をスローします。例外を無視して結果を取得し、同時に例外をログに記録するにはどうすればよいですか?

タスクを内部例外try{...}catch(){...}である別のタスクにラップすることはできますが、それらにアクセスできないため、このオーバーヘッドを追加する必要がないことを願っています。

14
Menelaos Vergis

Task.WhenAllの代わりに使用する次のようなメソッドを作成できます。

public Task<ResultOrException<T>[]> WhenAllOrException<T>(IEnumerable<Task<T>> tasks)
{    
    return Task.WhenAll(
        tasks.Select(
            task => task.ContinueWith(
                t => t.IsFaulted
                    ? new ResultOrException<T>(t.Exception)
                    : new ResultOrException<T>(t.Result))));
}


public class ResultOrException<T>
{
    public ResultOrException(T result)
    {
        IsSuccess = true;
        Result = result;
    }

    public ResultOrException(Exception ex)
    {
        IsSuccess = false;
        Exception = ex;
    }

    public bool IsSuccess { get; }
    public T Result { get; }
    public Exception Exception { get; }
}

次に、各結果をチェックして、成功したかどうかを確認できます。


編集:上記のコードはキャンセルを処理しません。代替の実装は次のとおりです。

public Task<ResultOrException<T>[]> WhenAllOrException<T>(IEnumerable<Task<T>> tasks)
{    
    return Task.WhenAll(tasks.Select(task => WrapResultOrException(task)));
}

private async Task<ResultOrException<T>> WrapResultOrException<T>(Task<T> task)
{
    try
    {           
        var result = await task;
        return new ResultOrException<T>(result);
    }
    catch (Exception ex)
    {
        return new ResultOrException<T>(ex);
    }
}
16
Thomas Levesque

例外処理を使用してHOCを追加し、成功を確認できます。

 class Program
{
    static async Task Main(string[] args)
    {
        var itemsToProcess = new[] { "one", "two" };
        var results = itemsToProcess.ToDictionary(x => x, async (item) =>
        {
            try
            {
                var result = await DoAsync();
                return ((Exception)null, result);
            }
            catch (Exception ex)
            {
                return (ex, (object)null);
            }
        });

        await Task.WhenAll(results.Values);

        foreach(var item in results)
        {
            Console.WriteLine(item.Key + (await item.Value).Item1 != null ? " Failed" : "Succeed");
        }
    }

    public static async Task<object> DoAsync()
    {
        await Task.Delay(10);
        throw new InvalidOperationException();
    }
}
0
OxQ