Здравствуйте, paradok, Вы писали:
S>>Там и так все понятно, разница на порядки. Ну могу дописать ожидание завершения всех задач, но это ничего не изменит. P>да, сделай ! там еще можно будет посмотреть зависимость от задержки, при 10 сек вообще кранты наступают системе.
S>Вопрос: как сделать, чтобы асинхронный метод отрабатывал так же быстро?
Подозреваю что суть проблемы в присутсвие синхронного ожидания Thread.Sleep(1000) после await.
Если я правильно понял ситуацию, то после await Task.Delay(1); оно уже выполняеться не на свеже-запущенном longRunning потоке
а на первом попавшемся из пула. Запущенный уже к этому моменту благополучно помер.
Соотвественно код выполняеться за время 1 *1000 / (размер пулла потоков)
Решение — обернуть синхронное ожидание в await Task.Factory.StartNew(longRunning), а из первоначального запуска задач этот параметр убрать.
Тогда, все что можно сделать на пуле-будет сделано на пуле, остальное на 1000 фоновых потоков.
Лучше оно от этого, по-сравнению с синхронным вариантом работать не будет, но будет хотя-бы так же криво. а не сильно хуже.
Ну еще можно побаловться с контекстом синхронизации, что замаскирет проблему и код станет таким же кривым и бажным как UI Windows
Чтобы работало нормально- надо лезть в библиотеку и переделывать ту синхронную функцию которую у вас символизирует Thread.Sleep(1000)
Здравствуйте, Shmj, Вы писали:
S>Добавил в стартовом сообщении.
Вынудил запустить. Переписал чтобы копипастой не заниматьсся:
using System;
using System.Diagnostics;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
namespace SyncAsync
{
static class Program
{
static async Task AsyncMethod(int i)
{
await Task.Delay(1); // Много разных асинхронных вызовов...
await Task.Delay(1);
// Это убрать нельзя - эмуляция вызова асинхронного метода, который мы изменить не можем.
Thread.Sleep(1000);
Console.WriteLine(i);
}
static void SyncMethod(int i)
{
Task.Delay(1).Wait(); // Много разных асинхронных вызовов обессинхрониваем - не комильфо...
Task.Delay(1).Wait();
// Это убрать нельзя - эмуляция вызова асинхронного метода, который мы изменить не можем.
Thread.Sleep(1000);
Console.WriteLine(i);
}
private static async Task RunAsync(string name, Func<int, Task> action)
{
var sw = new Stopwatch();
Console.WriteLine($"Start {name}");
sw.Start();
var tasks = Enumerable
.Range(0, 1000)
.Select(AsyncMethod);
await Task.WhenAll(tasks);
sw.Stop();
Console.WriteLine($"Done. ElapsedMilliseconds={sw.ElapsedMilliseconds}");
}
static async Task Main()
{
await RunAsync("Sync", i => Task.Factory.StartNew(() => SyncMethod(i), TaskCreationOptions.LongRunning));
await RunAsync("Async", async i => await AsyncMethod(i));
}
}
}
Первый варинт 38 сек, второй — 22 секунды. Что вполне логично, учитывая что размер пула воркер-потоков все таки поменьше 1000 единиц, в отличие от твоей 1 секунды на все потоки, каждый из которых блокируется на секунду.
Здравствуйте, Teolog, Вы писали:
T>Ну еще можно побаловться с контекстом синхронизации, что замаскирет проблему и код станет таким же кривым и бажным как UI Windows
Вот это интересно посмотреть...
T>Чтобы работало нормально- надо лезть в библиотеку и переделывать ту синхронную функцию которую у вас символизирует Thread.Sleep(1000)
Вы что весь мир будете переделывать под async? Знаете сколько библиотек, которые игнорят Task'и?
Здравствуйте, Ночной Смотрящий, Вы писали:
НС>Здравствуйте, Shmj, Вы писали:
S>>Добавил в стартовом сообщении.
НС>Вынудил запустить. Переписал чтобы копипастой не заниматьсся: НС>
Здравствуйте, Ночной Смотрящий, Вы писали:
НС>Первый варинт 38 сек, второй — 22 секунды. Что вполне логично, учитывая что размер пула воркер-потоков все таки поменьше 1000 единиц, в отличие от твоей 1 секунды на все потоки, каждый из которых блокируется на секунду.
Вы что решарпер не используете? У вас же описка — передаете action но забываете его задействовать.
Рекомендую пользоваться профессиональными инструментами, чтобы не совершать таких ошибок.
S>Вы что весь мир будете переделывать под async? Знаете сколько библиотек, которые игнорят Task'и?
Кто, я? Да чур меня, делать мне больше нечего.
Суть async/await в использовании кооперативной многозадачности для уменьшения количества ждущих потоков.
Проблема та-же что и во времена windows 3.11 — при плохом поведении любого кода в дальнем углу, встает колом всё.
В общем виде, проблема не решаеться никак,но в пределах своего кода и надежных библиотек требуемый результат достижим.
Поэтому этот самый async постепенно расползаеться везде где возможно ожидание данных. S>Знаете сколько библиотек, которые игнорят Task'и?
Вероятно много, однако любая библиотека без async работающая со вводом-выводом и периферийными устройствами строем идет нафиг, сразу как находится замена.
Здравствуйте, Shmj, Вы писали:
S>Здравствуйте, alexander_r, Вы писали:
S>>>...Синхронная версия отрабатывает 1-2 сек., асинхронная 1-2 мин. _>>а как вы время замеряете, покажите весь код??
S>Там и так все понятно, разница на порядки. Ну могу дописать ожидание завершения всех задач, но это ничего не изменит.
не знаю откуда у вас там разица на порядок
у меня ваш синхронный метод и тот что переделал Serginio1 выполняется за одинаковое время ~7.5сек
static void SyncMethod(int i)
{
Task.Delay(1).Wait(); // Много разных асинхронных вызовов обессинхрониваем - не комильфо...
Task.Delay(1).Wait();
// Это убрать нельзя - эмуляция вызова асинхронного метода, который мы изменить не можем.
Thread.Sleep(1000);
Console.WriteLine(i);
}
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 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));var tasks = Enumerable
.Range(0, 1000)
.Select(i => AsyncMethod(i));
await Task.WhenAll(tasks);
sw.Stop();
Console.WriteLine("Done!");
Console.WriteLine($"ElapsedMilliseconds={sw.ElapsedMilliseconds}");
Console.ReadKey();
}
Здравствуйте, alexander_r, Вы писали:
_>не знаю откуда у вас там разица на порядок _>у меня ваш синхронный метод и тот что переделал Serginio1 выполняется за одинаковое время ~7.5сек
Serginio1 предлагает добавлять обертку для каждого вызова синхронного метода, который мы можем подозревать в длительном исполнении — а это уродство.
Здравствуйте, Shmj, Вы писали:
S>Serginio1 предлагает добавлять обертку для каждого вызова синхронного метода, который мы можем подозревать в длительном исполнении — а это уродство.
т.е проблемы с производительностью нет, проблема в уродстве??
Здравствуйте, Danchik, Вы писали:
D> или поправьте коментарий что, вы делаете эмуляцию Синхронного вызова.
Он к тому что есть библиотеки где есть использование потоков и ожидание выполнения другого потока итд.
А использовать он хочет его в асинхронном коде. И говорит, что есть простое решение.
А завертывание долгого синхронного кода
Здравствуйте, Serginio1, Вы писали:
S>А использовать он хочет его в асинхронном коде. И говорит, что есть простое решение. S>А завертывание долгого синхронного кода
S>
Здравствуйте, Shmj, Вы писали:
S>Ага. Представьте 5 вызовов в методе (чередуются с синхронным) и 5 ваших гармошек с Task.Factory.StartNew...
S>На каждый вызов вы создаете по отдельному потому. А в синхронной версии всего 1 поток на метод. Тут не просто не красиво — тут еще и не оптимально.
Еще раз разработчик библиотеки не должен смешивать работу с задачами и потоками! Это разные парадигмы и есть асинхронные аналоги.
Если жевсе же ты решишл использовать, то ССЗС и тогда нужно профилировать код.
Ибо есть короткий и долгий код. Вот для долгого то кода и нужен отдельныая задача с LongRunning, что бы не останавливал поток из пула.
А все остальные используй как синхронный.
Мне интересно какое решение у тебя без использования профайлера
и солнце б утром не вставало, когда бы не было меня
Здравствуйте, Shmj, Вы писали:
S>Сразу код: синхронный вариант и асинхронный вариант метода (SyncMethod и AsyncMethod):
S>Итак, если запустить код — то увидим, что синхронный вариант отработает за 1-2 секунды а асинхронный — около 1 минуты (примерно, не ждал до конца). Но есть минус — в синхронном варианте внутри мы вызываем внешние асинхронные методы и каждый раз вынуждены их обессинхронивать — что не гуд.
S>Вопрос: как сделать, чтобы асинхронный метод отрабатывал так же быстро?
Установить мин. кол-во потоков в пуле в 1000 (ThreadPool.SetMinThreads). Это не бесплатно ессн, но если очень надо .
Связанно в политикой создания новых потоков в пуле, по умолчанию насколько я помню когда кол-во потоков в пуле превышает некоторое значение (где-то кол-во ядер * на некоторую константу), то добавление нового потока в пул происходит с некоторой задержкой (если память не изменяет чуть ли не секунду), и возможно эта задержка даже динамически увеличивается в зависимости от текущего размера пула.
Здравствуйте, pilgrim_, Вы писали:
_>Установить мин. кол-во потоков в пуле в 1000 (ThreadPool.SetMinThreads). Это не бесплатно ессн, но если очень надо .
Эх, жаль что вы мой козырь раскрыли. Думал может кто-то что-то нормальное предложит, но похоже другого варианта просто нет
Здравствуйте, Shmj, Вы писали:
S>Здравствуйте, pilgrim_, Вы писали:
_>>Установить мин. кол-во потоков в пуле в 1000 (ThreadPool.SetMinThreads). Это не бесплатно ессн, но если очень надо .
S>Эх, жаль что вы мой козырь раскрыли. Думал может кто-то что-то нормальное предложит, но похоже другого варианта просто нет
Ну да. Пул из 1000 потоков это коронное решение?
По моему мое решение с профайлером намного оптимальнее!
и солнце б утром не вставало, когда бы не было меня