Виконайте паралельно дві задачі на асинхронізацію та збирайте результати в .NET 4.5


116

Я деякий час намагався отримати щось, на що думав, що це буде просто працювати з .NET 4.5

Я хочу звільнити одночасно два тривалі завдання та збирати
результати найкращим чином C # 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>від виклику методу асинхронізації.

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;
}

Примітка: використання методу Go async не має значення
Simon_Weaver

3
Блок відбувається task1.Resultне в var task1 = Sleep(5000)тому випадку, оскільки метод сну без ключового слова, що очікує, є синхронним.
Арвіс

Відповіді:


86

Ви повинні використовувати 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;
        }
    }

11
Це чудова відповідь ... але я вважав, що це неправильна відповідь, поки я не зіткнувся з нею. тоді я зрозумів. Це дійсно виконується за 5 секунд. Хитрість полягає в тому, щоб НЕ чекати завдань негайно, а замість цього чекати на Task.WhenAll.
Тім Ловелл-Сміт

113
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
}

2
Я +1, тому що ви оголошуєте t1, t2 як Завдання, це правильний шлях.
Мінімум

12
Я вважаю, що це рішення вимагає, щоб метод Go також був асинхронним, тобто він виявляє можливість бути асинхронним. Якщо ви хотіли чогось більшого, як випадок запитувачів, коли Goметод абонента синхронний, але хоче виконати два незалежні завдання асинхронно (тобто жодне не потрібно виконувати перед іншим, але обидва повинні виконуватись, перш ніж виконання буде продовжено), тоді Task.WaitAllбуло б краще, і ви не не потрібне ключове слово, яке очікує, тому Goметод виклику не потребує самої асинхронізації. Жоден підхід не є кращим, це лише питання, яка ваша мета.
AaronLS

1
Недійсний метод: async void LongTask1() {...}не має властивості Task.Result. Використовуйте задачу без Т в такому випадку: async Task LongTask1().
Арвіс

Я не отримав результатів жодного із завдань. Тож я змінив його на Task<TResult> t1 = LongTask1();тепер і отримую t1.Result. <TResult>- це тип повернення результату. Для цього вам знадобиться return <TResult>у вашому методі.
gilu

1
Можливо, варто згадати, що якщо ви робите кілька дійсно простих речей і не хочете зайвих t1та t2змінних, ви можете використовувати new Task(...). Наприклад: int int1 = 0; await Task.WhenAll(new Task(async () => { int1 = await LongTask1(); }));. Одним із прихильників такого підходу є те, що компілятор не визнає, що змінна була призначена, і вважатиме її непризначеною, якщо ви не призначите їй початкове значення.
Роберт Денніс

3

Хоча ваш Sleepметод асинхронний, Thread.Sleepце не так. Вся ідея async полягає в повторному використанні однієї нитки, а не в запуску декількох потоків. Оскільки ви заблокували використання синхронного дзвінка до Thread.Sleep, це не спрацює.

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

Якщо вам потрібно запустити кілька синхронних блокуючих дзвінків, погляньте в іншому місці, я думаю!


дякую Річард - так, здається, працює так, як очікувалося, коли я фактично використовую свій сервісний дзвінок
Simon_Weaver

то як запустити асинхронізацію? У мене є додаток, зробити багато перемикання файлів і чекати , поки файл, близько 5 секунди, а потім ще один процес, коли я «коли для всіх» це перший запустити перший, а потім другий, навіть якщо я сказав: var x = y()і не var x=await y()чи y().wait()поки ще його чекати весь шлях, і якщо async не впорається з цим сам по собі, що мені робити? Зверніть увагу, що y прикрашено асинхронією, і я очікую, що це зробить все в межах, коли всі, а не в тому місці, куди його призначено, EDIT: я просто, коли мій партнер сказав, давайте спробуємо Task.Factory, і він сказав, що це спрацювало, коли я вибуду сторона цього класу
мертвийManN

2

Щоб відповісти на це питання:

Я хочу, щоб сон був асинхронним методом, щоб він міг чекати інших методів

ви можете, можливо, переписати 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

2

Зараз вихідні !

    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;
    }

0

Ця стаття допомогла пояснити багато речей. Це в стилі FAQ.

Асинхронізація / чекайте FAQ

Ця частина пояснює, чому Thread.Sleepпрацює на одній оригінальній нитці - це призводить до моєї первісної плутанини.

Чи викликає ключове слово "асинхронізація" виклик методу в чергу до ThreadPool? Щоб створити нову нитку? Для запуску ракетного корабля на Марс?

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

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.