[Этюд] - синхронный vs асинхронный
От: Shmj Ниоткуда  
Дата: 29.09.21 02:32
Оценка: -2 :)
Что-то давно этюдов не было. Не знаю тянет ли это на этюд, но попробую.

Сразу код: синхронный вариант и асинхронный вариант метода (SyncMethod и AsyncMethod):

  Синхронный
using System;
using System.Diagnostics;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;

namespace ConsoleApp29
{
    class Program
    {
        static void SyncMethod(int i)
        {
            Task.Delay(1).Wait(); // Много разных асинхронных вызовов обессинхрониваем - не комильфо...
            Task.Delay(1).Wait();

            // Это убрать нельзя - эмуляция вызова асинхронного метода, который мы изменить не можем.
            Thread.Sleep(1000);

            Console.WriteLine(i);
        }

        static async Task Main(string[] args)
        {
            var sw = new Stopwatch();

            Console.WriteLine("Start");

            sw.Start();

            var tasks = Enumerable
                .Range(0, 1000)
                .Select(i => Task.Factory.StartNew(() => { SyncMethod(i); }, TaskCreationOptions.LongRunning));

            await Task.WhenAll(tasks);

            sw.Stop();

            Console.WriteLine("Done!");
            Console.WriteLine($"ElapsedMilliseconds={sw.ElapsedMilliseconds}");
        }
    }
}


и

  Асинхронный
using System;
using System.Diagnostics;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;

namespace ConsoleApp29
{
    class Program
    {
        static async Task AsyncMethod(int i)
        {
            await Task.Delay(1); // Много разных асинхронных вызовов...
            await Task.Delay(1);

            // Это убрать нельзя - эмуляция вызова асинхронного метода, который мы изменить не можем.
            Thread.Sleep(1000);

            Console.WriteLine(i);
        }


        static async Task Main(string[] args)
        {
            var sw = new Stopwatch();

            Console.WriteLine("Start");

            sw.Start();

            var tasks = Enumerable
                .Range(0, 1000)
                .Select(AsyncMethod);

            await Task.WhenAll(tasks);

            sw.Stop();

            Console.WriteLine("Done!");
            Console.WriteLine($"ElapsedMilliseconds={sw.ElapsedMilliseconds}");
        }
    }
}


Метод внутри себя имеет синхронный длительный вызов (эмулируем с помощью Thread.Sleep), который во внешней библиотеке и переписать который в асинхронном варианте мы не можем.

Итак, если запустить код — то увидим, что синхронный вариант отработает за 1-2 секунды а асинхронный — около 1 минуты (примерно, не ждал до конца). Но есть минус — в синхронном варианте внутри мы вызываем внешние асинхронные методы и каждый раз вынуждены их обессинхронивать — что не гуд.

Вопрос: как сделать, чтобы асинхронный метод отрабатывал так же быстро?
Отредактировано 30.09.2021 10:30 Shmj . Предыдущая версия . Еще …
Отредактировано 29.09.2021 9:26 Shmj . Предыдущая версия .
Отредактировано 29.09.2021 2:33 Shmj . Предыдущая версия .
Re: [Этюд] - синхронный vs асинхронный
От: Serginio1 СССР https://habrahabr.ru/users/serginio1/topics/
Дата: 29.09.21 07:06
Оценка:
Здравствуйте, Shmj, Вы писали:

S>Вопрос: как сделать, чтобы асинхронный метод отрабатывал так же быстро?


А почему AsyncMethod(iCopy).Wait();
а не await AsyncMethod(iCopy)
И зачем в асинхронном еще и отдельный ask.Factory.StartNew
https://docs.microsoft.com/ru-ru/dotnet/csharp/language-reference/proposals/csharp-7.1/async-main
и солнце б утром не вставало, когда бы не было меня
Отредактировано 29.09.2021 9:19 Serginio1 . Предыдущая версия .
Re: [Этюд] - синхронный vs асинхронный
От: BlackEric http://black-eric.lj.ru
Дата: 29.09.21 09:07
Оценка:
Здравствуйте, Shmj, Вы писали:

S>Вопрос: как сделать, чтобы асинхронный метод отрабатывал так же быстро?


Wait() блокирует поток исполнения. Нужно как минимум переписать через await, а потом уже смотреть что получается по скорости ища профайлером узкое место.
https://github.com/BlackEric001
Re[2]: [Этюд] - синхронный vs асинхронный
От: Shmj Ниоткуда  
Дата: 29.09.21 09:17
Оценка:
Здравствуйте, Serginio1, Вы писали:

S>А почему AsyncMethod(iCopy).Wait();

S>а не await AsyncMethod(iCopy)
S>https://docs.microsoft.com/ru-ru/dotnet/csharp/language-reference/proposals/csharp-7.1/async-main

Без разницы — это проблему не решит.

Переписал без него — разницы нет.
Отредактировано 29.09.2021 9:26 Shmj . Предыдущая версия .
Re[2]: [Этюд] - синхронный vs асинхронный
От: Shmj Ниоткуда  
Дата: 29.09.21 09:18
Оценка:
Здравствуйте, BlackEric, Вы писали:

BE>Wait() блокирует поток исполнения. Нужно как минимум переписать через await, а потом уже смотреть что получается по скорости ища профайлером узкое место.


Это проблему не решит абсолютно. Переписал без него, чтобы не резало глаз.
Отредактировано 29.09.2021 9:26 Shmj . Предыдущая версия .
Re[3]: [Этюд] - синхронный vs асинхронный
От: Serginio1 СССР https://habrahabr.ru/users/serginio1/topics/
Дата: 29.09.21 09:20
Оценка: +1
Здравствуйте, Shmj, Вы писали:

S>Здравствуйте, Serginio1, Вы писали:


S>>А почему AsyncMethod(iCopy).Wait();

S>>а не await AsyncMethod(iCopy)
S>>https://docs.microsoft.com/ru-ru/dotnet/csharp/language-reference/proposals/csharp-7.1/async-main

S>Без разницы — это проблему не решит.

И зачем там отдельный Task.Factory.StartNew и тем более без LongRunning

То есть ты уже пробовал?
Там LongRunning запускает отдельные потоки для каждой задачи. 1000 потоков при переключении могут и тормозить.
и солнце б утром не вставало, когда бы не было меня
Отредактировано 29.09.2021 9:23 Serginio1 . Предыдущая версия . Еще …
Отредактировано 29.09.2021 9:22 Serginio1 . Предыдущая версия .
Re[4]: [Этюд] - синхронный vs асинхронный
От: Shmj Ниоткуда  
Дата: 29.09.21 09:28
Оценка:
Здравствуйте, Serginio1, Вы писали:

S> И зачем там отдельный Task.Factory.StartNew и тем более без LongRunning


Где Task.Factory.StartNew без LongRunning?

Только один раз и только с LongRunning.

S>То есть ты уже пробовал?

S> Там LongRunning запускает отдельные потоки для каждой задачи. 1000 потоков при переключении могут и тормозить.

Попробуйте убрать LongRunning — без него и первый вариант начнет тормозить.
Отредактировано 29.09.2021 9:28 Shmj . Предыдущая версия .
Re[5]: [Этюд] - синхронный vs асинхронный
От: Serginio1 СССР https://habrahabr.ru/users/serginio1/topics/
Дата: 29.09.21 10:27
Оценка:
Здравствуйте, Shmj, Вы писали:

S>Попробуйте убрать LongRunning — без него и первый вариант начнет тормозить.


То есть такой код тоже тормозит?

static async Task Main(string[] args)
        {
            for (int i = 0; i < 1000; i++)
            {
                int iCopy = i;

              await AsyncMethod(iCopy);

            }
            Console.ReadKey();
        }

Тогда у тебя будет ожидаться одна задача, то есть задачи выполняются последовательно
По сути то ты хочешь выполнить все задачи без ожидания выполнения других. Убери await


static async Task Main(string[] args)
        {
   Task[] tasks=new Task[1000];
            for (int i = 0; i < 1000; i++)
            {
                int iCopy = i;

              tasks[i]=AsyncMethod(iCopy);

            }
 await Task.WhenAll(tasks);
            Console.ReadKey();
        }
и солнце б утром не вставало, когда бы не было меня
Отредактировано 29.09.2021 10:38 Serginio1 . Предыдущая версия . Еще …
Отредактировано 29.09.2021 10:33 Serginio1 . Предыдущая версия .
Отредактировано 29.09.2021 10:32 Serginio1 . Предыдущая версия .
Re[6]: [Этюд] - синхронный vs асинхронный
От: Shmj Ниоткуда  
Дата: 29.09.21 10:36
Оценка:
Здравствуйте, Serginio1, Вы писали:

S>
S>static async Task Main(string[] args)
S>        {
S>   Task[] tasks=new Task[1000];
S>            for (int i = 0; i < 1000; i++)
S>            {
S>                int iCopy = i;

S>              tasks[i]=AsyncMethod(iCopy);

S>            }
S> await Task.WhenAll(tasks);
S>            Console.ReadKey();
S>        }
S>


Попробуйте запустить код перед тем как публиковать — этот код выполняется несколько минут, а 1 вариант — 2 секунды.
Re[7]: [Этюд] - синхронный vs асинхронный
От: Serginio1 СССР https://habrahabr.ru/users/serginio1/topics/
Дата: 29.09.21 10:42
Оценка:
Здравствуйте, Shmj, Вы писали:

Ну Thread.Sleep(1000); в сихронном варианте переключает потоки
И проще 1000 потоков запустить. А вот для пула потоков это хреново, так как тогда надо и пул потоков расширять.
Поэтому и нехорошо совмещать методы с потоками и задачами.
Ты внутри задачи которая лонг вызываешь васинхронный код который внутри в итоге и вызывает Thread.Sleep(1000)
и солнце б утром не вставало, когда бы не было меня
Отредактировано 29.09.2021 10:44 Serginio1 . Предыдущая версия .
Re[8]: [Этюд] - синхронный vs асинхронный
От: Shmj Ниоткуда  
Дата: 29.09.21 10:50
Оценка:
Здравствуйте, Serginio1, Вы писали:

S>Поэтому и нехорошо совмещать методы с потоками и задачами.


Может и не хорошо, но из асинхронных методов приходится вызывать сторонние синхронные, в которых есть аналоги Thread.Sleep(1000) в том или ином варианте.

Мы же не сможем все методы переписать под себя, все сделать асинхронными.

Так же из этого же метода приходится вызывать синхронные.

Что же делать?

S> Ты внутри задачи которая лонг вызываешь васинхронный код который внутри в итоге и вызывает Thread.Sleep(1000)


Предлагайте решение.
Re[9]: [Этюд] - синхронный vs асинхронный
От: Serginio1 СССР https://habrahabr.ru/users/serginio1/topics/
Дата: 29.09.21 10:58
Оценка: +2
Здравствуйте, Shmj, Вы писали:

S>Так же из этого же метода приходится вызывать синхронные.


S>Что же делать?


S>> Ты внутри задачи которая лонг вызываешь асинхронный код который внутри в итоге и вызывает Thread.Sleep(1000)


S>Предлагайте решение.

Тут и так понятно если ты вызваешь долгий синхронный код, то и работай с ним синхронно.
Асинхронный код не поможет, а только засрет пул потоков. Или выделяй отдельно долгий синхронный метод в Task c LongRunning

В данном случае
 await Task.Factory.StartNew(() =>
                {
                   Thread.Sleep(1000);
                }, TaskCreationOptions.LongRunning);


class Program
    {
        static async Task AsyncMethod(int i)
        {
            await Task.Delay(1); // Много разных асинхронных вызовов...
            await Task.Delay(1);

            // Это убрать нельзя - эмуляция вызова асинхронного метода, который мы изменить не можем.
            //Thread.Sleep(1000);
            await Task.Factory.StartNew(() =>
                {
                   Thread.Sleep(1000);
                }, TaskCreationOptions.LongRunning);

            Console.WriteLine(i);
        }

        static void Main(string[] args)
        {
            for (int i = 0; i < 1000; i++)
            {
                int iCopy = i;

                // Вариант 2: асинхронный метод отрабатывате медленно (TaskCreationOptions.LongRunning не применяется)
                Task.Factory.StartNew(async () =>
                {
                    await AsyncMethod(iCopy);
                });
            }


            Console.ReadKey();
        }
    }
и солнце б утром не вставало, когда бы не было меня
Отредактировано 30.09.2021 6:54 Serginio1 . Предыдущая версия . Еще …
Отредактировано 29.09.2021 11:07 Serginio1 . Предыдущая версия .
Отредактировано 29.09.2021 11:06 Serginio1 . Предыдущая версия .
Отредактировано 29.09.2021 11:04 Serginio1 . Предыдущая версия .
Re: [Этюд] - синхронный vs асинхронный
От: alexander_r  
Дата: 29.09.21 11:22
Оценка:
Здравствуйте, Shmj, Вы писали:
S>Итак, если запустить код — то увидим, что синхронный вариант отработает за 1-2 секунды а асинхронный — около 1 минуты (примерно, не ждал до конца).
А что вы замеряете время исполнения цикла от for (int i = 0; i < 1000; i++) до Console.ReadKey();
или итоговое время выполнения всех задач т.е когда последняя задача завершится ??
Re[10]: [Этюд] - синхронный vs асинхронный
От: Shmj Ниоткуда  
Дата: 29.09.21 11:50
Оценка:
Здравствуйте, Serginio1, Вы писали:

S> Тут и так понятно если ты вызваешь долгий синхронный код, то и работай с ним синхронно.


Так там в одном методе вызов и синхронных и асинхронных внешних методов — вот в чем фишка.

S>Асинхронный код не поможет, а только засрет пул потоков. Или выделяй отдельно долгий синхронный метод в Task c LongRunning


А если там много разных методов синхронных, не явно какие из них долгие? Все оборачивать предлагаете?
Re[2]: [Этюд] - синхронный vs асинхронный
От: Shmj Ниоткуда  
Дата: 29.09.21 11:51
Оценка:
Здравствуйте, alexander_r, Вы писали:

_>А что вы замеряете время исполнения цикла от for (int i = 0; i < 1000; i++) до Console.ReadKey();

_>или итоговое время выполнения всех задач т.е когда последняя задача завершится ??

Конечно итоговое исполнение всех задач.
Re[11]: [Этюд] - синхронный vs асинхронный
От: Serginio1 СССР https://habrahabr.ru/users/serginio1/topics/
Дата: 29.09.21 12:10
Оценка:
Здравствуйте, Shmj, Вы писали:

S>>Асинхронный код не поможет, а только засрет пул потоков. Или выделяй отдельно долгий синхронный метод в Task c LongRunning


S>А если там много разных методов синхронных, не явно какие из них долгие? Все оборачивать предлагаете?

Есть профайлер. И таких методов немного
и солнце б утром не вставало, когда бы не было меня
Re[12]: [Этюд] - синхронный vs асинхронный
От: Shmj Ниоткуда  
Дата: 29.09.21 12:13
Оценка:
Здравствуйте, Serginio1, Вы писали:

S>>А если там много разных методов синхронных, не явно какие из них долгие? Все оборачивать предлагаете?

S> Есть профайлер. И таких методов немного

Вы можете заранее не знать реализацию метода — работать через контракты, конкретная реализация выбирается в рантайме в зависимости от данных.

Есть способ сделать проще — не оборачивая каждый метод в отдельности и не заморачиваться с профайлером.
Re[13]: [Этюд] - синхронный vs асинхронный
От: Serginio1 СССР https://habrahabr.ru/users/serginio1/topics/
Дата: 29.09.21 12:16
Оценка:
Здравствуйте, Shmj, Вы писали:

S>Здравствуйте, Serginio1, Вы писали:


S>>>А если там много разных методов синхронных, не явно какие из них долгие? Все оборачивать предлагаете?

S>> Есть профайлер. И таких методов немного

S>Вы можете заранее не знать реализацию метода — работать через контракты, конкретная реализация выбирается в рантайме в зависимости от данных.


S>Есть способ сделать проще — не оборачивая каждый метод в отдельности и не заморачиваться с профайлером.

Так я тебе и написал. С синхронным кодом работай синхронно. Смешивать ненужно.
Если же хочешь оптимизировать то выделяй весь синхронный код в LongRunning
и солнце б утром не вставало, когда бы не было меня
Re[14]: [Этюд] - синхронный vs асинхронный
От: Shmj Ниоткуда  
Дата: 29.09.21 12:20
Оценка:
Здравствуйте, Serginio1, Вы писали:

S> Так я тебе и написал. С синхронным кодом работай синхронно. Смешивать ненужно.


Смешивать или нет — это зависит от сторонних библиотек. Часть библиотек имеет асинхронную реализацию, часть синхронную. И все это нужно вызвать в одном методе.

S>Если же хочешь оптимизировать то выделяй весь синхронный код в LongRunning


Получается криво — внутри метода много однотипных оберток. Можно сделать все проще.
Re[15]: [Этюд] - синхронный vs асинхронный
От: Serginio1 СССР https://habrahabr.ru/users/serginio1/topics/
Дата: 29.09.21 12:22
Оценка:
Здравствуйте, Shmj, Вы писали:
S>Получается криво — внутри метода много однотипных оберток. Можно сделать все проще.
Угу нужно пользоваться нормальными библиотеками. Которые не совмещают работу с потоками и задачами
и солнце б утром не вставало, когда бы не было меня
Отредактировано 29.09.2021 12:25 Serginio1 . Предыдущая версия .
Подождите ...
Wait...
Пока на собственное сообщение не было ответов, его можно удалить.