Запуск двух асинхронных задач параллельно и сбор результатов в .NET 4.5

Я пытался какое-то время получить что-то, что, как я думал, будет просто работать с .NET 4.5

Я хочу запустить две длительные задачи одновременно и собрать   приводит к лучшему пути С# 4.5 (RTM).

Следующие работы, но мне это не нравится, потому что:

  • Я хочу Sleep быть асинхронным методом, чтобы он мог await другие методы
  • Он просто выглядит неуклюжим с Task.Run()
  • Я не думаю, что это даже использование каких-либо новых функций языка вообще!

Рабочий код:

public static void Go()
{
    Console.WriteLine("Starting");

    var task1 = Task.Run(() => Sleep(5000));    
    var task2 = Task.Run(() => Sleep(3000));

    int totalSlept = task1.Result + task2.Result;

    Console.WriteLine("Slept for a total of " + totalSlept + " ms");
}

private static int Sleep(int ms)
{
    Console.WriteLine("Sleeping for " + ms);
    Thread.Sleep(ms);
    Console.WriteLine("Sleeping for " + ms + " FINISHED");
    return ms;
}

Нерабочий код:

Обновление: это действительно работает, и это правильный способ сделать это, единственная проблема - это Thread.Sleep

Этот код не работает, потому что вызов Sleep(5000) сразу запускает задачу, поэтому Sleep(1000) не запускается, пока она не завершится. Это верно, даже если Sleep - async, и я не использую await или слишком быстро вызываю .Result.

Я подумал, что, возможно, есть способ получить нерабочий Task<T>, вызвав метод async, чтобы я мог затем вызвать Start() для двух задач, но я не могу понять, как получить Task<T> от вызова метода async.

public static void Go()
{
    Console.WriteLine("Starting");

    var task1 = Sleep(5000);    // blocks
    var task2 = Sleep(1000);

    int totalSlept = task1.Result + task2.Result;

    Console.WriteLine("Slept for " + totalSlept + " ms");
}

private static async Task<int> Sleep(int ms)
{
    Console.WriteLine("Sleeping for " + ms);
    Thread.Sleep(ms);
    return ms;
}

Ответ 1

Вы должны использовать Task.Delay вместо Sleep для асинхронного программирования, а затем использовать Task.WhenAll для объединения результатов задачи. Задачи будут выполняться параллельно.

public class Program
    {
        static void Main(string[] args)
        {
            Go();
        }
        public static void Go()
        {
            GoAsync();
            Console.ReadLine();
        }
        public static async void GoAsync()
        {

            Console.WriteLine("Starting");

            var task1 = Sleep(5000);
            var task2 = Sleep(3000);

            int[] result = await Task.WhenAll(task1, task2);

            Console.WriteLine("Slept for a total of " + result.Sum() + " ms");

        }

        private async static Task<int> Sleep(int ms)
        {
            Console.WriteLine("Sleeping for {0} at {1}", ms, Environment.TickCount);
            await Task.Delay(ms);
            Console.WriteLine("Sleeping for {0} finished at {1}", ms, Environment.TickCount);
            return ms;
        }
    }

Ответ 2

async Task<int> LongTask1() { 
  ...
  return 0; 
}

async Task<int> LongTask2() { 
  ...
  return 1; 
}

...
{
   Task<int> t1 = LongTask1();
   Task<int> t2 = LongTask2();
   await Task.WhenAll(t1,t2);
   //now we have t1.Result and t2.Result
}

Ответ 3

Пока ваш метод Sleep является асинхронным, Thread.Sleep - нет. Вся идея async заключается в повторном использовании одного потока, а не для запуска нескольких потоков. Поскольку вы заблокировали использование синхронного вызова Thread.Sleep, он не будет работать.

Я предполагаю, что Thread.Sleep является упрощением того, что вы на самом деле хотите сделать. Может ли ваша фактическая реализация кодироваться как асинхронные методы?

Если вам нужно запустить несколько синхронных блокирующих вызовов, посмотрите в другом месте, я думаю!

Ответ 4

Чтобы ответить на этот вопрос:

Я хочу, чтобы Sleep был асинхронным методом, чтобы он мог ожидать других методов.

вы можете переписать функцию Sleep следующим образом:

private static async Task<int> Sleep(int ms)
{
    Console.WriteLine("Sleeping for " + ms);
    var task = Task.Run(() => Thread.Sleep(ms));
    await task;
    Console.WriteLine("Sleeping for " + ms + "END");
    return ms;
}

static void Main(string[] args)
{
    Console.WriteLine("Starting");

    var task1 = Sleep(2000);
    var task2 = Sleep(1000);

    int totalSlept = task1.Result +task2.Result;

    Console.WriteLine("Slept for " + totalSlept + " ms");
    Console.ReadKey();
}

запускает этот код:

Starting
Sleeping for 2000
Sleeping for 1000
*(one second later)*
Sleeping for 1000END
*(one second later)*
Sleeping for 2000END
Slept for 3000 ms

Ответ 5

Сейчас выходные !

    public async void Go()
    {
        Console.WriteLine("Start fosterage...");

        var t1 = Sleep(5000, "Kevin");
        var t2 = Sleep(3000, "Jerry");
        var result = await Task.WhenAll(t1, t2);

        Console.WriteLine($"My precious spare time last for only {result.Max()}ms");
        Console.WriteLine("Press any key and take same beer...");
        Console.ReadKey();
    }

    private static async Task<int> Sleep(int ms, string name)
    {
            Console.WriteLine($"{name} going to sleep for {ms}ms :)");
            await Task.Delay(ms);
            Console.WriteLine("${name} waked up after {ms}ms :(";
            return ms;
    }

Ответ 6

Эта статья помогла объяснить многое. Это в стиле FAQ.

Часто задаваемые вопросы по Async/Await

В этой части объясняется, почему Thread.Sleep работает в том же исходном потоке, что приводит к моей первоначальной путанице.

Является ли ключевое слово "async" причиной вызова метода в очередь ThreadPool? Создать новый поток? Запустить ракетный корабль в Марс?

Нет. Нет. И нет. См. Предыдущие вопросы. Ключевое слово "async" указывает компилятору, что "ожидание" может использоваться внутри способа, так что способ может приостанавливаться в точке ожидания и его выполнение возобновляется асинхронно, когда ожидаемый экземпляр завершается. Вот почему компилятор выдает предупреждение, если нет "ожидает" внутри метода, помеченного как "асинхронный".

Ответ 7

К сожалению, если у вас есть еще один метод, Task.WhenAll() вам не поможет. Асинхронные методы не являются параллельными.

Для реального параллельного выполнения вы должны запускать новые задачи вручную, например, используя Task.Run() или ConfigureAwait (false).

Подробности смотрите здесь: https://www.wintellect.com/tasks-are-still-not-threads-and-async-is-not-parallel/