Как выполнить несколько задач в. NET Core и проверить, что успешно завершено - PullRequest
0 голосов
/ 02 апреля 2020

Я пытаюсь выполнить следующий сценарий:

  • Создать несколько задач
    • все задачи находятся в одной структуре (один код с разными параметрами)
    • структура такова: попытаться сделать, поймать, если не удалось, и выдать исключение / исключение
  • Запустить их параллельно и дождаться их завершения
  • После завершения проверьте, какие задачи вызвали исключение, а какие были выполнены успешно, без создаваемого исключения
public class Controller : ControllerBase
    {
        private readonly List<string> _names = new List<string>()
        {
            "name1",
            "name2"
        };

        [HttpGet]
        public async Task<ActionResult> Get()
        {
            // Leaving this processing because it is the same in the original code, maybe there is something here that is relevant
            var tasks = _names.ToDictionary(name => name, name => ExecuteRequest(name, async (value) =>
            {
                return await ReturnOrThrow(value);
            }));

            // I want to wait until all were finished
            await Task.WhenAll(tasks.Values); // This already throws an exception, i don't want it to
            // I don't want to catch exception here and just ignore it

            var namesThatSucceeded = tasks.Count(t => t.Value.IsCompletedSuccessfully);
            var namesThatThrewException = tasks.Count(t => t.Value.IsFaulted);

            return Ok(new
            {
                Succeeded = namesThatSucceeded,
                Failed = namesThatThrewException
            });
        }

        // The "generic task structure" that runs the request, catches exception if thrown, and re-throws it.
        private async Task<string> ExecuteRequest(string name, Func<string, Task<string>> request)
        {
            try
            {
                return await request(name);
            }
            catch (HttpRequestException e)
            {
                Console.WriteLine(e.Message);
                throw; // I would prefer to just return Faulted Task here so it won't throw exception
            }
        }

        // The actual processing
        private async Task<string> ReturnOrThrow(string name)
        {
            if (name == "name1")
            {
                throw new HttpRequestException();
            }

            return await Task.FromResult(name);
        }
    }

Ответы [ 4 ]

1 голос
/ 03 апреля 2020

Вы можете обернуть действие с помощью функции высокого порядка, которая имеет обработчик ожидания и исключения.

 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 голосов
/ 03 апреля 2020

Простой способ подавить исключение ожидаемой задачи - передать задачу в качестве единственного аргумента Task.WhenAny:

Создает задачу, которая завершится, когда Любая из поставленных задач выполнена.

await Task.WhenAny(Task.WhenAll(tasks.Values)); // Ignores the exception

Это работает, потому что задача, возвращаемая из Task.WhenAny, никогда не завершается сбоем. Когда он завершается, он всегда завершается успешно. Однако есть два небольших недостатка:

  1. Он не сообщает четко о своем намерении, поэтому рекомендуется добавить комментарий.
  2. Этот метод принимает аргумент params Task[], поэтому его вызывают приводит к выделению объекта (не используйте его в горячих путях).
0 голосов
/ 02 апреля 2020

Вы не можете избежать исключения в ожидании Task.WhenAll (). Это поведение по замыслу. Но вы можете передать статус задания через его значение Result. Просто расширите Result от string до (string Result, bool Success) и верните false Успех из catch без повторного выброса исключения.

public class Controller : ControllerBase
{
    private readonly List<string> _names = new List<string>()
    {
        "name1",
        "name2"
    };

    [HttpGet]
    public async Task<ActionResult> Get()
    {
        // Leaving this processing because it is the same in the original code, maybe there is something here that is relevant
        var tasks = _names.ToDictionary(name => name, name => ExecuteRequest(name, async (value) =>
        {
            return await ReturnOrThrow(value);
        }));

        await Task.WhenAll(tasks.Values); // Doesn't throw exception anymore, but you can access Success status from Task's Result tuple

        var namesThatSucceeded = tasks.Count(t => t.Value.Result.Success);
        var namesThatThrewException = tasks.Count(t => !t.Value.Result.Success);

        return Ok(new
        {
            Succeeded = namesThatSucceeded,
            Failed = namesThatThrewException
        });
    }

    // The "generic task structure" that runs the request, catches exception if thrown, and re-throws it.
    private async Task<(string Result, bool Success)> ExecuteRequest(string name, Func<string, Task<string>> request)
    {
        try
        {
            return (await request(name), true);
        }
        catch (HttpRequestException e)
        {
            Console.WriteLine(e.Message);
            return (null, false);
        }
    }

    // The actual processing
    private async Task<string> ReturnOrThrow(string name)
    {
        if (name == "name1")
        {
            throw new HttpRequestException();
        }

        return await Task.FromResult(name);
    }
}

Не за что.

0 голосов
/ 02 апреля 2020

Вы пропустили ожидание в ToDictionary, где вы вызываете ExecuteRequest?

async name => await ExecuteRequest
...