Что-то давно этюдов не было. Не знаю тянет ли это на этюд, но попробую.
Сразу код: синхронный вариант и асинхронный вариант метода (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 минуты (примерно, не ждал до конца). Но есть минус — в синхронном варианте внутри мы вызываем внешние асинхронные методы и каждый раз вынуждены их обессинхронивать — что не гуд.
Вопрос: как сделать, чтобы асинхронный метод отрабатывал так же быстро?
Здравствуйте, Shmj, Вы писали:
S>Вопрос: как сделать, чтобы асинхронный метод отрабатывал так же быстро?
Wait() блокирует поток исполнения. Нужно как минимум переписать через await, а потом уже смотреть что получается по скорости ища профайлером узкое место.
Здравствуйте, BlackEric, Вы писали:
BE>Wait() блокирует поток исполнения. Нужно как минимум переписать через await, а потом уже смотреть что получается по скорости ища профайлером узкое место.
Это проблему не решит абсолютно. Переписал без него, чтобы не резало глаз.
Здравствуйте, Serginio1, Вы писали:
S> И зачем там отдельный Task.Factory.StartNew и тем более без LongRunning
Где Task.Factory.StartNew без LongRunning?
Только один раз и только с LongRunning.
S>То есть ты уже пробовал? S> Там LongRunning запускает отдельные потоки для каждой задачи. 1000 потоков при переключении могут и тормозить.
Попробуйте убрать LongRunning — без него и первый вариант начнет тормозить.
Здравствуйте, 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();
}
и солнце б утром не вставало, когда бы не было меня
Ну Thread.Sleep(1000); в сихронном варианте переключает потоки
И проще 1000 потоков запустить. А вот для пула потоков это хреново, так как тогда надо и пул потоков расширять.
Поэтому и нехорошо совмещать методы с потоками и задачами.
Ты внутри задачи которая лонг вызываешь васинхронный код который внутри в итоге и вызывает Thread.Sleep(1000)
и солнце б утром не вставало, когда бы не было меня
Здравствуйте, Serginio1, Вы писали:
S>Поэтому и нехорошо совмещать методы с потоками и задачами.
Может и не хорошо, но из асинхронных методов приходится вызывать сторонние синхронные, в которых есть аналоги Thread.Sleep(1000) в том или ином варианте.
Мы же не сможем все методы переписать под себя, все сделать асинхронными.
Так же из этого же метода приходится вызывать синхронные.
Что же делать?
S> Ты внутри задачи которая лонг вызываешь васинхронный код который внутри в итоге и вызывает Thread.Sleep(1000)
Здравствуйте, Shmj, Вы писали:
S>Так же из этого же метода приходится вызывать синхронные.
S>Что же делать?
S>> Ты внутри задачи которая лонг вызываешь асинхронный код который внутри в итоге и вызывает Thread.Sleep(1000)
S>Предлагайте решение.
Тут и так понятно если ты вызваешь долгий синхронный код, то и работай с ним синхронно.
Асинхронный код не поможет, а только засрет пул потоков. Или выделяй отдельно долгий синхронный метод в Task c 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();
}
}
и солнце б утром не вставало, когда бы не было меня
Здравствуйте, Shmj, Вы писали: S>Итак, если запустить код — то увидим, что синхронный вариант отработает за 1-2 секунды а асинхронный — около 1 минуты (примерно, не ждал до конца).
А что вы замеряете время исполнения цикла от for (int i = 0; i < 1000; i++) до Console.ReadKey();
или итоговое время выполнения всех задач т.е когда последняя задача завершится ??
Здравствуйте, Serginio1, Вы писали:
S> Тут и так понятно если ты вызваешь долгий синхронный код, то и работай с ним синхронно.
Так там в одном методе вызов и синхронных и асинхронных внешних методов — вот в чем фишка.
S>Асинхронный код не поможет, а только засрет пул потоков. Или выделяй отдельно долгий синхронный метод в Task c LongRunning
А если там много разных методов синхронных, не явно какие из них долгие? Все оборачивать предлагаете?
Здравствуйте, alexander_r, Вы писали:
_>А что вы замеряете время исполнения цикла от for (int i = 0; i < 1000; i++) до Console.ReadKey(); _>или итоговое время выполнения всех задач т.е когда последняя задача завершится ??
Здравствуйте, Shmj, Вы писали:
S>>Асинхронный код не поможет, а только засрет пул потоков. Или выделяй отдельно долгий синхронный метод в Task c LongRunning
S>А если там много разных методов синхронных, не явно какие из них долгие? Все оборачивать предлагаете?
Есть профайлер. И таких методов немного
и солнце б утром не вставало, когда бы не было меня
Здравствуйте, Serginio1, Вы писали:
S>>А если там много разных методов синхронных, не явно какие из них долгие? Все оборачивать предлагаете? S> Есть профайлер. И таких методов немного
Вы можете заранее не знать реализацию метода — работать через контракты, конкретная реализация выбирается в рантайме в зависимости от данных.
Есть способ сделать проще — не оборачивая каждый метод в отдельности и не заморачиваться с профайлером.
Здравствуйте, Shmj, Вы писали:
S>Здравствуйте, Serginio1, Вы писали:
S>>>А если там много разных методов синхронных, не явно какие из них долгие? Все оборачивать предлагаете? S>> Есть профайлер. И таких методов немного
S>Вы можете заранее не знать реализацию метода — работать через контракты, конкретная реализация выбирается в рантайме в зависимости от данных.
S>Есть способ сделать проще — не оборачивая каждый метод в отдельности и не заморачиваться с профайлером.
Так я тебе и написал. С синхронным кодом работай синхронно. Смешивать ненужно.
Если же хочешь оптимизировать то выделяй весь синхронный код в LongRunning
и солнце б утром не вставало, когда бы не было меня
Здравствуйте, Serginio1, Вы писали:
S> Так я тебе и написал. С синхронным кодом работай синхронно. Смешивать ненужно.
Смешивать или нет — это зависит от сторонних библиотек. Часть библиотек имеет асинхронную реализацию, часть синхронную. И все это нужно вызвать в одном методе.
S>Если же хочешь оптимизировать то выделяй весь синхронный код в LongRunning
Получается криво — внутри метода много однотипных оберток. Можно сделать все проще.
Здравствуйте, Shmj, Вы писали: S>Получается криво — внутри метода много однотипных оберток. Можно сделать все проще.
Угу нужно пользоваться нормальными библиотеками. Которые не совмещают работу с потоками и задачами
и солнце б утром не вставало, когда бы не было меня