Что-то давно этюдов не было. Не знаю тянет ли это на этюд, но попробую.
Сразу код: синхронный вариант и асинхронный вариант метода (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 минуты (примерно, не ждал до конца). Но есть минус — в синхронном варианте внутри мы вызываем внешние асинхронные методы и каждый раз вынуждены их обессинхронивать — что не гуд.
Вопрос: как сделать, чтобы асинхронный метод отрабатывал так же быстро?
S>>Что жалко что ли на 64 битке она практически бесконечна. S> Проблема не в памяти, а в создании и переключении потоков. S> И не забывай в твоем варианте метод практически не использует данных, а вот если будет использовать стек то он и физически будет расти. S>То есть ты сознательно тормозишь свое приложения. Вместо того что бы при большом одновременном количестве запросов ставить их в очередь ты запускаешь новые или берешь древние потоки. S>Там и кэш процессора сбрасывается.
По ссылке не ходил, но речь идет о процессах, а не о потоках -- потоки как раз придумали, чтобы не сбрасывать
кэш и т.п. Т.е. переключение процессов дорого, а потоков в процессе -- нет.
Здравствуйте, 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>Здравствуйте, 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();
}
Здравствуйте, Sharov, Вы писали:
S>Здравствуйте, Danchik, Вы писали:
S>>>А чем плохо пул из 1 млн. потоков? Они же не будут создаваться без необходимости? D>>Один поток — 4MB на стек для x64.
S>Вроде бы 1Мб всегда был? Или в core что-то поменялось?
Здравствуйте, Shmj, Вы писали:
S>Сразу код: синхронный вариант и асинхронный вариант метода (SyncMethod и AsyncMethod):
S>Итак, если запустить код — то увидим, что синхронный вариант отработает за 1-2 секунды а асинхронный — около 1 минуты (примерно, не ждал до конца). Но есть минус — в синхронном варианте внутри мы вызываем внешние асинхронные методы и каждый раз вынуждены их обессинхронивать — что не гуд.
S>Вопрос: как сделать, чтобы асинхронный метод отрабатывал так же быстро?
Установить мин. кол-во потоков в пуле в 1000 (ThreadPool.SetMinThreads). Это не бесплатно ессн, но если очень надо .
Связанно в политикой создания новых потоков в пуле, по умолчанию насколько я помню когда кол-во потоков в пуле превышает некоторое значение (где-то кол-во ядер * на некоторую константу), то добавление нового потока в пул происходит с некоторой задержкой (если память не изменяет чуть ли не секунду), и возможно эта задержка даже динамически увеличивается в зависимости от текущего размера пула.
Здравствуйте, Shmj, Вы писали:
S>Здравствуйте, Serginio1, Вы писали:
S>>>Каким образом? S>> Заверни весь синхронный код в Task с LongRunning
S>Если вызовов синхронных методов много (чередуются с async) и как долго они исполняются — не определено (зависит от данных) — предлагаете заворачивать каждый в отдельности? Как завернуть 1 раз для всех — вот в чем вопрос.
Ждать развития PGO, аналогом HotSpot https://github.com/dotnet/runtime/blob/main/docs/design/features/DynamicPgo.md
и солнце б утром не вставало, когда бы не было меня
Здравствуйте, alexander_r, Вы писали:
_>Здравствуйте, Shmj, Вы писали:
S>>...Синхронная версия отрабатывает 1-2 сек., асинхронная 1-2 мин. _>а как вы время замеряете, покажите весь код??
там же номер запуска — т.е. когда появляется на экране 999 тогда и конец.
ну или когда останавливается визуально мельтешение цифер в консоли.
Для усиления эффекта можно задержку не 1 сек а 10 сек сделать
Вы попробуйте — катастрофа тормоза настолько грандиозна, что и так видно без всяких измерений
— у меня разница 100 раз.
Возможно на мега супер компе будет меньше...
У меня начинаются тормоза с i=10 ! не 1000 а с 10 !!
Это вообще крест а асинках — их нельзя юзать если может вызваться больше 10 обработчиков одновременно?
Здравствуйте, 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 секунды на все потоки, каждый из которых блокируется на секунду.
Здравствуйте, Ночной Смотрящий, Вы писали:
НС>Здравствуйте, Shmj, Вы писали:
S>>Добавил в стартовом сообщении.
НС>Вынудил запустить. Переписал чтобы копипастой не заниматьсся: НС>
Здравствуйте, Ночной Смотрящий, Вы писали:
НС>Первый варинт 38 сек, второй — 22 секунды. Что вполне логично, учитывая что размер пула воркер-потоков все таки поменьше 1000 единиц, в отличие от твоей 1 секунды на все потоки, каждый из которых блокируется на секунду.
Вы что решарпер не используете? У вас же описка — передаете action но забываете его задействовать.
Рекомендую пользоваться профессиональными инструментами, чтобы не совершать таких ошибок.
Здравствуйте, Danchik, Вы писали:
D> или поправьте коментарий что, вы делаете эмуляцию Синхронного вызова.
Он к тому что есть библиотеки где есть использование потоков и ожидание выполнения другого потока итд.
А использовать он хочет его в асинхронном коде. И говорит, что есть простое решение.
А завертывание долгого синхронного кода
Здравствуйте, pilgrim_, Вы писали:
_>Установить мин. кол-во потоков в пуле в 1000 (ThreadPool.SetMinThreads). Это не бесплатно ессн, но если очень надо .
Эх, жаль что вы мой козырь раскрыли. Думал может кто-то что-то нормальное предложит, но похоже другого варианта просто нет
Здравствуйте, Shmj, Вы писали: S>А чем плохо пул из 1 млн. потоков
Caution
By default, the minimum number of threads is set to the number of processors on a system. You can use the SetMinThreads method to increase the minimum number of threads. However, unnecessarily increasing these values can cause performance problems. If too many tasks start at the same time, all of them might appear to be slow. In most cases, the thread pool will perform better with its own algorithm for allocating threads. Reducing the minimum to less than the number of processors can also hurt performance.
Здравствуйте, 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>Итак, если запустить код — то увидим, что синхронный вариант отработает за 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>Получается криво — внутри метода много однотипных оберток. Можно сделать все проще.
Угу нужно пользоваться нормальными библиотеками. Которые не совмещают работу с потоками и задачами
и солнце б утром не вставало, когда бы не было меня
Здравствуйте, Serginio1, Вы писали:
S>>Получается криво — внутри метода много однотипных оберток. Можно сделать все проще. S>Угу нужно пользоваться нормальными библиотеками. Которые не совмещают работу с потоками и задачами
Это две разные библиотеки. Она через потоки, другая через задачи. И вызывать их нужно в одном методе несколько раз. Оборачивать каждый вызов обычного метода в Task.Factory.StartNew — не красиво.
Здравствуйте, Shmj, Вы писали:
S>Здравствуйте, Serginio1, Вы писали:
S>>>Получается криво — внутри метода много однотипных оберток. Можно сделать все проще. S>>Угу нужно пользоваться нормальными библиотеками. Которые не совмещают работу с потоками и задачами
S>Это две разные библиотеки. Она через потоки, другая через задачи. И вызывать их нужно в одном методе несколько раз. Оборачивать каждый вызов обычного метода в Task.Factory.StartNew — не красиво.
Сделай класс наследник и добавь асинхронные методы через LongRunning или через SG
и солнце б утром не вставало, когда бы не было меня
Здравствуйте, Shmj, Вы писали:
S>Здравствуйте, Serginio1, Вы писали:
S>>Сделай класс наследник и добавь асинхронные методы через LongRunning или через SG
S>Т.е. вы таки весь мир хотите переделать на async? Для каждой библиотеки делать со-библиотеку наследника/обертку с асинхронной версией?
S>А нельзя ли без этой лишней работы?
Я предлагаю использовать нормальные асинхронные библиотеки, а не скрещивать работу с потоками и задачами.
Да для работы с асинхронным кодом должны быть и асинхронные библиотеки. На Windows Phone изначально не было методов с ожиданием выполнения другого потока или ожидание IO.
Если метод долгий то его нужно выделять в LongRunning
и солнце б утром не вставало, когда бы не было меня
Здравствуйте, Shmj, Вы писали:
S>Почему нельзя 1 раз указать этот LongRunning?
Можешь. Просто если метод быстрый (который может и проинлайниться), то зачем запускать отдельный поток?
Смысл асинхронного кода как раз в том, что есть немного синхронного кода, но большая часть кода выполняется в другом процессе (базы данных, HTTP сервера,TCP)
либо IO. Длинные методы как правило распараллеливаются и можно отдельно выделять LongRunning но таких методов немного
и солнце б утром не вставало, когда бы не было меня
Здравствуйте, Shmj, Вы писали:
S>Здравствуйте, Serginio1, Вы писали:
S>>>Почему нельзя 1 раз указать этот LongRunning? S>> Можешь.
S>Каким образом?
Заверни весь синхронный код в Task с LongRunning
и солнце б утром не вставало, когда бы не было меня
Здравствуйте, Serginio1, Вы писали:
S>>Каким образом? S> Заверни весь синхронный код в Task с LongRunning
Если вызовов синхронных методов много (чередуются с async) и как долго они исполняются — не определено (зависит от данных) — предлагаете заворачивать каждый в отдельности? Как завернуть 1 раз для всех — вот в чем вопрос.
Здравствуйте, Shmj, Вы писали:
S>Что-то давно этюдов не было. Не знаю тянет ли это на этюд, но попробую.
У тебя какая то фигня написана. Ты ни там ни там не ждешь завершения задач и непонятно что измеряешь. Лень экспериментировать, но разница, скорее всего, связана с настройкой IsBackground потоков.
Тебе надо что то вроде:
Здравствуйте, Ночной Смотрящий, Вы писали:
НС>У тебя какая то фигня написана. Ты ни там ни там не ждешь завершения задач и непонятно что измеряешь. Лень экспериментировать, но разница, скорее всего, связана с настройкой IsBackground потоков.
Там по завершении задачи на экран выводится номер задачи. Синхронная версия отрабатывает 1-2 сек., асинхронная 1-2 мин.
Здравствуйте, alexander_r, Вы писали:
S>>...Синхронная версия отрабатывает 1-2 сек., асинхронная 1-2 мин. _>а как вы время замеряете, покажите весь код??
Там и так все понятно, разница на порядки. Ну могу дописать ожидание завершения всех задач, но это ничего не изменит.
Здравствуйте, Shmj, Вы писали:
S>Здравствуйте, alexander_r, Вы писали:
S>>>...Синхронная версия отрабатывает 1-2 сек., асинхронная 1-2 мин. _>>а как вы время замеряете, покажите весь код??
S>Там и так все понятно, разница на порядки. Ну могу дописать ожидание завершения всех задач, но это ничего не изменит.
да, сделай ! там еще можно будет посмотреть зависимость от задержки, при 10 сек вообще кранты наступают системе.
Здравствуйте, 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)
Здравствуйте, Teolog, Вы писали:
T>Ну еще можно побаловться с контекстом синхронизации, что замаскирет проблему и код станет таким же кривым и бажным как UI Windows
Вот это интересно посмотреть...
T>Чтобы работало нормально- надо лезть в библиотеку и переделывать ту синхронную функцию которую у вас символизирует Thread.Sleep(1000)
Вы что весь мир будете переделывать под async? Знаете сколько библиотек, которые игнорят Task'и?
S>Вы что весь мир будете переделывать под async? Знаете сколько библиотек, которые игнорят Task'и?
Кто, я? Да чур меня, делать мне больше нечего.
Суть async/await в использовании кооперативной многозадачности для уменьшения количества ждущих потоков.
Проблема та-же что и во времена windows 3.11 — при плохом поведении любого кода в дальнем углу, встает колом всё.
В общем виде, проблема не решаеться никак,но в пределах своего кода и надежных библиотек требуемый результат достижим.
Поэтому этот самый async постепенно расползаеться везде где возможно ожидание данных. S>Знаете сколько библиотек, которые игнорят Task'и?
Вероятно много, однако любая библиотека без async работающая со вводом-выводом и периферийными устройствами строем идет нафиг, сразу как находится замена.
Здравствуйте, alexander_r, Вы писали:
_>не знаю откуда у вас там разица на порядок _>у меня ваш синхронный метод и тот что переделал Serginio1 выполняется за одинаковое время ~7.5сек
Serginio1 предлагает добавлять обертку для каждого вызова синхронного метода, который мы можем подозревать в длительном исполнении — а это уродство.
Здравствуйте, Shmj, Вы писали:
S>Serginio1 предлагает добавлять обертку для каждого вызова синхронного метода, который мы можем подозревать в длительном исполнении — а это уродство.
т.е проблемы с производительностью нет, проблема в уродстве??
Здравствуйте, Serginio1, Вы писали:
S>А использовать он хочет его в асинхронном коде. И говорит, что есть простое решение. S>А завертывание долгого синхронного кода
S>
Здравствуйте, Shmj, Вы писали:
S>Ага. Представьте 5 вызовов в методе (чередуются с синхронным) и 5 ваших гармошек с Task.Factory.StartNew...
S>На каждый вызов вы создаете по отдельному потому. А в синхронной версии всего 1 поток на метод. Тут не просто не красиво — тут еще и не оптимально.
Еще раз разработчик библиотеки не должен смешивать работу с задачами и потоками! Это разные парадигмы и есть асинхронные аналоги.
Если жевсе же ты решишл использовать, то ССЗС и тогда нужно профилировать код.
Ибо есть короткий и долгий код. Вот для долгого то кода и нужен отдельныая задача с LongRunning, что бы не останавливал поток из пула.
А все остальные используй как синхронный.
Мне интересно какое решение у тебя без использования профайлера
и солнце б утром не вставало, когда бы не было меня
Здравствуйте, Shmj, Вы писали:
S>Здравствуйте, pilgrim_, Вы писали:
_>>Установить мин. кол-во потоков в пуле в 1000 (ThreadPool.SetMinThreads). Это не бесплатно ессн, но если очень надо .
S>Эх, жаль что вы мой козырь раскрыли. Думал может кто-то что-то нормальное предложит, но похоже другого варианта просто нет
Ну да. Пул из 1000 потоков это коронное решение?
По моему мое решение с профайлером намного оптимальнее!
и солнце б утром не вставало, когда бы не было меня
Здравствуйте, Shmj, Вы писали:
S>А чем плохо пул из 1 млн. потоков? Они же не будут создаваться без необходимости?
Почему не будут. Когда у тебя стоит ограничение, то лишние задачи встают в очередь и новых потоков не создается,
а когда нет и много одновременных задач не встают в очередь и есть затраты на создание потока и на переключение между потоками http://rsdn.org/forum/philosophy/8038767.1
Здравствуйте, Shmj, Вы писали:
S>Здравствуйте, Serginio1, Вы писали:
S>> Ну да. Пул из 1000 потоков это коронное решение? S>>По моему мое решение с профайлером намного оптимальнее!
S>А чем плохо пул из 1 млн. потоков? Они же не будут создаваться без необходимости?
Один поток — 4MB на стек для x64. Множим на миллион и уходим в своп. Или точнее в никуда, 4 терабайта это серьезно
Здравствуйте, Shmj, Вы писали:
S>Создал 10 тыс. потоков в ожидании, заняло 270 Мб. памяти. Получается примерно по 27 Кб на поток. Так что не так страшен черт...
А можно скрин соотв. фото из Process Exploer'а, ибо 270Мб это физически потребленная память,
а виртуально там 10^4 * 10^6 байт (10Гб вроде?).
Здравствуйте, Sharov, Вы писали:
S>А можно скрин соотв. фото из Process Exploer'а, ибо 270Мб это физически потребленная память, S>а виртуально там 10^4 * 10^6 байт (10Гб вроде?).
Запустите код и сообщите что увидели, вам это тоже нужно знать:
private static List<Thread> _threads = new List<Thread>();
static async Task Main(string[] args)
{
ThreadPool.SetMinThreads(1000000, 1);
for (int i = 0; i < 10000; i++)
{
var t = new Thread(() =>
{
Thread.Sleep(100000);
})
{IsBackground = true};
t.Start();
_threads.Add(t);
}
Console.WriteLine("Done");
Console.ReadKey();
}
Здравствуйте, Shmj, Вы писали:
S>>А можно скрин соотв. фото из Process Exploer'а, ибо 270Мб это физически потребленная память, S>>а виртуально там 10^4 * 10^6 байт (10Гб вроде?). S>Запустите код и сообщите что увидели, вам это тоже нужно знать:
Ну так что? Сколько реально потребили 10 тыс. потоков? Вряд ли у вас в приложении столько будет, верно?
S>Ну и, кучу виртуальной памяти зарезервировано.
Что жалко что ли на 64 битке она практически бесконечна.
Здравствуйте, Shmj, Вы писали:
S>Что жалко что ли на 64 битке она практически бесконечна.
Проблема не в памяти, а в создании и переключении потоков.
И не забывай в твоем варианте метод практически не использует данных, а вот если будет использовать стек то он и физически будет расти.
То есть ты сознательно тормозишь свое приложения. Вместо того что бы при большом одновременном количестве запросов ставить их в очередь ты запускаешь новые или берешь древние потоки.
Там и кэш процессора сбрасывается. Переключение контекста и производительность
Кроме того, что очень важно, при переключении контекста происходят следующие программно-незаметные аппаратные действия, влияющие на производительность:
Происходит очистка конвейера команд и данных процессора
Очищается TLB, отвечающий за страничное отображение линейных адресов на физические.
Кроме того, следует учесть следующие факты, влияющие на состояние системы:
Содержимое кэша (особенно это касается кэша первого уровня), накопленное и «оптимизированное» под выполнение одного потока, оказывается совершенно неприменимым к новому потоку, на который происходит переключение.
При переключении контекста на процесс, который до этого долгое время не использовался (см. Подкачка страниц), многие страницы могут физически отсутствовать в оперативной памяти, что порождает подгрузку вытесненных страниц из вторичной памяти.
Ну и если задачи короткие то они могут быть соизмеремы с временем переключения контекста
и солнце б утром не вставало, когда бы не было меня
D>Один поток — 4MB на стек для x64. Множим на миллион и уходим в своп. Или точнее в никуда, 4 терабайта это серьезно
Оно почти не кушает памяти само по себе, выделение страниц идет по требованию, а адресного пространства которое действительно выделяется -вагон.
Здравствуйте, Sharov, Вы писали:
S>По ссылке не ходил, но речь идет о процессах, а не о потоках -- потоки как раз придумали, чтобы не сбрасывать S>кэш и т.п. Т.е. переключение процессов дорого, а потоков в процессе -- нет.
Согласен, но процессов то может быть и не один. И так гонок за процессорное время будет больше
и солнце б утром не вставало, когда бы не было меня
Здравствуйте, Serginio1, Вы писали:
S>>По ссылке не ходил, но речь идет о процессах, а не о потоках -- потоки как раз придумали, чтобы не сбрасывать S>>кэш и т.п. Т.е. переключение процессов дорого, а потоков в процессе -- нет. S>Согласен, но процессов то может быть и не один. И так гонок за процессорное время будет больше
Речь идет о потоках в процессе. Остальное не интересно.
Здравствуйте, Sharov, Вы писали:
S>Здравствуйте, Serginio1, Вы писали:
S>>>По ссылке не ходил, но речь идет о процессах, а не о потоках -- потоки как раз придумали, чтобы не сбрасывать S>>>кэш и т.п. Т.е. переключение процессов дорого, а потоков в процессе -- нет. S>>Согласен, но процессов то может быть и не один. И так гонок за процессорное время будет больше
S>Речь идет о потоках в процессе. Остальное не интересно.
Да но, переключаться то приходится и на другие потоки процессов. Суть то в том, что хотят выделять пул потоков без ограничения.
А вот тут то как раз и возникают проблемы при переключении с на потоки с других процессов, гонки за потоки
и солнце б утром не вставало, когда бы не было меня
Здравствуйте, Serginio1, Вы писали:
S>>Речь идет о потоках в процессе. Остальное не интересно. S>Да но, переключаться то приходится и на другие потоки процессов. Суть то в том, что хотят выделять пул потоков без ограничения. S>А вот тут то как раз и возникают проблемы при переключении с на потоки с других процессов, гонки за потоки
Переключение между потоками дешево по сравнению с процессами.
Здравствуйте, Sharov, Вы писали:
S>Здравствуйте, Serginio1, Вы писали:
S>>>Речь идет о потоках в процессе. Остальное не интересно. S>>Да но, переключаться то приходится и на другие потоки процессов. Суть то в том, что хотят выделять пул потоков без ограничения. S>>А вот тут то как раз и возникают проблемы при переключении с на потоки с других процессов, гонки за потоки
S>Переключение между потоками дешево по сравнению с процессами.
Еще раз, для других процессов разве не нужны ядра для переключения на свои потоки?
И разве не ббудет гонок за потками если бездумно выделять огромное количество в пуле потоков?
и солнце б утром не вставало, когда бы не было меня
Здравствуйте, Sharov, Вы писали: S>>И разве не ббудет гонок за потками если бездумно выделять огромное количество в пуле потоков?
S>Что такое гонка за потоками? Если потоков много, гонки должно быть меньше. Разве нет?
Как раз гонок больше ибо работающих потоков больше и между ними нужно делить процессорное время.
Как раз и выбирают пул потоков близким количеству ядер.
>>По ссылке не ходил, но речь идет о процессах, а не о потоках -- потоки как раз придумали, чтобы не сбрасывать >>кэш и т.п. Т.е. переключение процессов дорого, а потоков в процессе -- нет.
Ну и опять же сброс кэша процессора может быть и в одном процессе,
тот же стек потока может сбрасываться на диск при недостатке памяти.
То есть два разных потока работают с разными данными. Это могут быть огромные массивы данных.
Там на время работы потока то данные кэша меняются
Как эти данные могут держаться в кэше?
и солнце б утром не вставало, когда бы не было меня