Выполнение многих долгосрочных задач с помощью шаблона проектирования «TAP» - PullRequest
1 голос
/ 22 апреля 2020

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

Мой вопрос, как мне запустить много длительных асинхронных функций внутри синхронной функции? Я знаю, что могу использовать Task.Run () , но мне кажется, что есть лучший способ. Если я просто попытаюсь запустить функцию как есть, появится предупреждение ...

"Поскольку этот вызов не ожидается, выполнение текущего метода продолжается до завершения вызова."

... появляется, что означает, что я делаю что-то не так ... или я? Какой самый эффективный и правильный способ заставить всех клиентов работать одновременно?

class AsyncClient 
{
    public AsyncClient() 
    {
        ...
    }

    public async Task RunAsync(IPAddress address, int port)
    {
        ... waiting for data
    }
}

static void Main(string[] args)
{
    List<AsyncClient> clients = new <AsyncClient>();

    clients.Add(new AsyncClient());
    clients.Add(new AsyncClient());
    clients.Add(new AsyncClient());

    foreach (var c in clients)
    {
        // What is the best way to start every async tasks?

        c.RunAsync("127.0.0.1", "8080");

        // ^ This gives the warning "Because this call is not awaited, 
        // execution of the current method continues before the call is completed."
    }
}

Спасибо!

Ответы [ 2 ]

3 голосов
/ 22 апреля 2020

Сначала вы должны изменить Main метод на async:

static async Task Main(string[] args)

Затем вы можете ожидать асинхронных операций.

Чтобы они могли работать параллельно, вы может использовать LINQ Select:

IEnumerable<Task> tasks = clients.Select(c => c.RunAsync("127.0.0.1", "8080"));
await Task.WhenAll(tasks);

Task.WhenAll возвращает новый Task, который завершается после завершения всех предоставленных Task.

Не ожидая Task s, есть хороший шанс, что ваш метод Main завершится, и, следовательно, программа выйдет, прежде чем Task s сразятся,

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

Итак, у вас есть не асинхронный c метод, и в этом не асинхронном c методе вы хотите вызывать асинхронные c методы.

Обычно метод асинхронный c, потому что где-то глубоко внутри вашей нити приходится ждать другого длинного процесса до конца sh. Подумайте о файле, который нужно записать, о запросе к базе данных или о некоторой информации, извлекаемой из inte rnet. Обычно это функции, в которых асин c методы находятся рядом с не-асин c методами.

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

Так что, если вы хотите делать другие вещи, пока другой процесс выполняет свою задачу: просто не ждите. Проблема, конечно, заключается в следующем: вы хотите узнать результат другой задачи, прежде чем ваша функция завершится. Если вы этого не сделаете, если вам будет сложно определить условия публикации вашего метода.

void MyMethod()
{
    Task<int> taskA = MethodAasync(...);

    // you didn't await, you are free to do something else, like calling another async method
    Task<double> taskB = MethodBasync(...);

    DoSomethingUseful();

    // if here, you need the result of taskA. Wait until it is ready
    // this blocks your thread!
    taskA.Wait();
    int resultA = taskA.Result();
    ProcessResult(resultA);

    // if desired, you can wait for a collection of tasks:
    Task[] tasksToWaitFor = new Task[] {taskA, taskB};
    Task.WaitAll(tasksToWaitFor);
    int resultA = taskA.Result();
    double resultB = taskB.Result();
    ProcessResults(resultA, resultB);
}

Даже если вас не интересует результат выполнения заданий, будет целесообразно дождаться их завершения sh. Это позволяет реагировать на исключения.

Кстати, вы видели, что я не звонил Task.Run! Что происходит, так это то, что мой поток входит в MethodAasyn c до тех пор, пока не увидит ожидание. Затем процедура возвращается к управлению, поэтому она входит в MethodBasyn c, пока не увидит ожидание. Ваша процедура возвращает управление DoSomethingUseful.

Как только другой процесс (запрос к базе данных, файл записи и т. Д. c) завершен, один из потоков пула потоков продолжает обрабатывать операторы после ожидания До тех пор, пока он не встретит новое ожидание, или пока больше нечего обрабатывать.

Task.Wait и Task.WaitAll - это методы, которые останавливают эту асинхронность: поток действительно блокируется до тех пор, пока все асинхронные c методы не будут полностью завершены.

Редко есть причина использовать Task.Run, если вы хотите вызвать асинхронный метод c: просто вызовите его, не ждите его, чтобы вы могли делать другие полезные вещи. Обязательно дождитесь, пока задача завершится sh, как только вам понадобится результат, или самое позднее, когда вы вернете метод.

Другой способ - вернуть задачи, не ожидая их завершения. sh, чтобы дать звонящему возможность сделать что-то полезное, если задачи не выполнены. Конечно, это может быть сделано только в том случае, если вашей процедуре не нужен результат задачи. Он также обязывает вашего абонента ждать завершения или передавать задачи своему вызывающему.

Единственная причина для Task.Run, которую я вижу, в том, что вы хотите запустить длительную процедуру в своем собственном процессе, что вы не хотите ждать прямо сейчас. Подумайте о длительных расчетах. Не используйте Task.Run, если задействован другой процесс. В этом случае другой процесс должен иметь функцию asyn c, или вы должны создать метод расширения asyn c, который выполняет задачу. Запуск.

int DoSomeLengthyCalculations(...) {...};

async Task<MyResult> CalculateIt(...)
{
    Task<int> taskLengthyCalculations = Task.Run( () => DoSomeLengthyCalculations(...);

     // if desired DoSomethingUsefull; after that wait for the task to end
     // and process the result:
     Task.Wait(taskLengthyCalculations);
     int resultLengthyCalculations = taskLengthyCalucalations.Result();
     MyResult result = ProcessResult(resultLengthyCalculations);
     return result;
}

Приятно, что вы скрыто ли вы делаете длительные вычисления, или что кто-то другой делает это. Например, если вы используете методы модульного тестирования, которые asyn c обращаются к базе данных, вы можете смоделировать это при обращении к словарю. }

...