Re[2]: Как оптимизировать выполнения 10000 параллельных задач?
От: LWhisper  
Дата: 04.08.16 11:05
Оценка:
Здравствуйте, gandjustas, Вы писали:

G>Конечно существуют. Основная рекомендация — не создавать потоки. Вообще забудь про thread.start. Забудь что у тебя есть потоки.

G>Используй асинхронный код, он сам заустит продолжение где надо и не будет заниматься фигней, при этом не съест потоков больше, чем в thread pool.
И как результат: асинхронные таски повисли на каком-нибудь _event.WaitOne(). Пул исчерпан. Новые потоки не стартсятся. Если раньше сервер жил несколько месяцев без перезагрузки, теперь он будет умирать каждый час.

Перейти от потоков к процессам, писать на чисто неблокирующий код без вечных вейтов, используя APC там, где это возможно. Перейти от потоковой модели к очереди задач и конечному автомату. В идеальном мире при отсутствии временных рамок и нелимитированных ресурсах, я бы поступил так. К сожалению, сейчас это невозможно.
Re[3]: Как оптимизировать выполнения 10000 параллельных зада
От: Serginio1 СССР https://habrahabr.ru/users/serginio1/topics/
Дата: 04.08.16 11:50
Оценка: +1
Здравствуйте, LWhisper, Вы писали:

LW>И как результат: асинхронные таски повисли на каком-нибудь _event.WaitOne(). Пул исчерпан. Новые потоки не стартсятся. Если раньше сервер жил несколько месяцев без перезагрузки, теперь он будет умирать каждый час.


Используй AsyncAutoResetEvent

https://msdn.microsoft.com/en-us/library/microsoft.visualstudio.threading.asyncautoresetevent.aspx
https://blogs.msdn.microsoft.com/pfxteam/2012/02/11/building-async-coordination-primitives-part-2-asyncautoresetevent/
https://github.com/StephenCleary/AsyncEx/wiki/AsyncAutoResetEvent

https://github.com/StephenCleary/AsyncEx
и солнце б утром не вставало, когда бы не было меня
Отредактировано 04.08.2016 12:29 Serginio1 . Предыдущая версия . Еще …
Отредактировано 04.08.2016 12:03 Serginio1 . Предыдущая версия .
Re[3]: Как оптимизировать выполнения 10000 параллельных задач?
От: gandjustas Россия http://blog.gandjustas.ru/
Дата: 04.08.16 19:34
Оценка: -1
Здравствуйте, LWhisper, Вы писали:

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


G>>Конечно существуют. Основная рекомендация — не создавать потоки. Вообще забудь про thread.start. Забудь что у тебя есть потоки.

G>>Используй асинхронный код, он сам заустит продолжение где надо и не будет заниматься фигней, при этом не съест потоков больше, чем в thread pool.
LW>И как результат: асинхронные таски повисли на каком-нибудь _event.WaitOne(). Пул исчерпан. Новые потоки не стартсятся. Если раньше сервер жил несколько месяцев без перезагрузки, теперь он будет умирать каждый час.
В асинхронных тасках нельзя использовать синхронное ожидание.

LW>Перейти от потоков к процессам, писать на чисто неблокирующий код без вечных вейтов, используя APC там, где это возможно. Перейти от потоковой модели к очереди задач и конечному автомату. В идеальном мире при отсутствии временных рамок и нелимитированных ресурсах, я бы поступил так. К сожалению, сейчас это невозможно.



Ты слишком хорошо знаешь winapi. Попробуй для профилактики решить ту же задачу на nodejs.
Re[4]: Как оптимизировать выполнения 10000 параллельных зада
От: LWhisper  
Дата: 05.08.16 12:54
Оценка:
Здравствуйте, Serginio1, Вы писали:

S>Используй AsyncAutoResetEvent


S>https://msdn.microsoft.com/en-us/library/microsoft.visualstudio.threading.asyncautoresetevent.aspx

S>https://blogs.msdn.microsoft.com/pfxteam/2012/02/11/building-async-coordination-primitives-part-2-asyncautoresetevent/
S>https://github.com/StephenCleary/AsyncEx/wiki/AsyncAutoResetEvent

S>https://github.com/StephenCleary/AsyncEx


Вопрос, как перейти в 524 местах от старых ивентов к новым, мы опустим.
Но я пока не понимаю, как вышеописанные примеры способны исправить ситуацию? В одном запускается таска из пула повисает на локе. В другом запускается таска и виснет в while(true). В третьем запускается таска и у неё вызывается Wait(). Всё это приведёт к вышеописанной ситуации — исчерпании пула потоков и заморозки работы процесса. В одном из трёх случаев поток переходит в сигнальное состояние, но никаких APC при этом не вызывается, так что он просто висит в ожидании.


        /// <summary>
        /// Asynchronously waits for this event to be set.
        /// </summary>
        public Task WaitAsync()
        {
            lock (_sync)
            {
                var ret = _tcs.Task;
                //Enlightenment.Trace.AsyncManualResetEvent_Wait(this, ret);
                return ret;
            }
        }


Возможно, я чего-то не понимаю? Повисший на локе await переводит поток в сигнальное состояние и тот выполняет другие задачи из пула?
Re[5]: Как оптимизировать выполнения 10000 параллельных зада
От: Serginio1 СССР https://habrahabr.ru/users/serginio1/topics/
Дата: 05.08.16 13:21
Оценка:
Здравствуйте, LWhisper, Вы писали:


LW>
LW>        /// <summary>
LW>        /// Asynchronously waits for this event to be set.
LW>        /// </summary>
LW>        public Task WaitAsync()
LW>        {
LW>            lock (_sync)
LW>            {
LW>                var ret = _tcs.Task;
LW>                //Enlightenment.Trace.AsyncManualResetEvent_Wait(this, ret);
LW>                return ret;
LW>            }
LW>        }
LW>


LW>Возможно, я чего-то не понимаю? Повисший на локе await переводит поток в сигнальное состояние и тот выполняет другие задачи из пула?


В асинхронном программировании все основано на событии и разбиении метода на участки которые выполняются по сигналу выполнения задачи используя конечный автомат.
Задача повисшая на локе await завершает свою работу в потоке и продолжит работу когда задача на await завершится. За этим следит планировщик.
У тебя будет куча задач висящих на await , но количество используемых потоков будет равно реально работающим.

Работа задач это облегченные fiber. Еще в энумераторах через yield MS пошла на сохранение состояние и продолжение по вызову.
Async await аналогично yield, только MoveNext() возникает по окончании задачи ждущей на await
и солнце б утром не вставало, когда бы не было меня
Отредактировано 05.08.2016 13:28 Serginio1 . Предыдущая версия .
Re[6]: Как оптимизировать выполнения 10000 параллельных зада
От: LWhisper  
Дата: 05.08.16 13:40
Оценка:
Здравствуйте, Serginio1, Вы писали:

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

S>Задача повисшая на локе await завершает свою работу в потоке и продолжит работу когда задача на await завершится. За этим следит планировщик.
S> У тебя будет куча задач висящих на await , но количество используемых потоков будет равно реально работающим.



Так в том то и дело, что изначально 10000 соединений никто не отменял.
Они порождают 10000 тасок. Эти 10000 тасок порождают 10000 потоков. Эти потоки повисают на якобы асинхронном локе. Поток не может вернуться волшебным образом в пул или переключиться на выполнение другой задачи. Он будет висеть до посинения. Единственное, что я могу сделать — это вручную нагрузить его полезной работой. Фактически, async просто предоставляет удобный интерфейс для формирования APC очереди. Просто помимо проблемы 10000 потоков, я получаю довеском проблему исчерпания пула потоков.
Re[7]: Как оптимизировать выполнения 10000 параллельных зада
От: Sharov Россия  
Дата: 05.08.16 16:13
Оценка:
Здравствуйте, LWhisper, Вы писали:

LW>Image: HcTflRn.png


Что призван продемонстрировать этот код? Зачем в lock Console.Readline()?
LW>Так в том то и дело, что изначально 10000 соединений никто не отменял.
LW>Они порождают 10000 тасок. Эти 10000 тасок порождают 10000 потоков. Эти потоки повисают на якобы асинхронном локе. Поток не может вернуться волшебным образом в пул или переключиться на выполнение другой задачи. Он будет висеть до посинения.

С чего это вдруг? Есть такое понятие как логическая блокировка -- задача висит до выполнения условия, а поток возвращается в пул.
Кодом людям нужно помогать!
Re[8]: Как оптимизировать выполнения 10000 параллельных зада
От: LWhisper  
Дата: 05.08.16 16:30
Оценка:
Здравствуйте, Sharov, Вы писали:

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


LW>>Image: HcTflRn.png


S>Что призван продемонстрировать этот код? Зачем в lock Console.Readline()?

S>С чего это вдруг? Есть такое понятие как логическая блокировка -- задача висит до выполнения условия, а поток возвращается в пул.
Просто вечная блокировка.
Скриншот выше демонстрирует обратное. Поток не возвращается в пул, а висит в ожидании блокировки. Было запущено 30 задач — 30 потоков из пула сели на lock и остались в таком состяонии. Вершины стеков в нижней части скриншота.
Re[7]: Как оптимизировать выполнения 10000 параллельных зада
От: Serginio1 СССР https://habrahabr.ru/users/serginio1/topics/
Дата: 05.08.16 17:05
Оценка:
Здравствуйте, LWhisper, Вы писали:

LW>Так в том то и дело, что изначально 10000 соединений никто не отменял.

LW>Они порождают 10000 тасок. Эти 10000 тасок порождают 10000 потоков. Эти потоки повисают на якобы асинхронном локе. Поток не может вернуться волшебным образом в пул или переключиться на выполнение другой задачи. Он будет висеть до посинения. Единственное, что я могу сделать — это вручную нагрузить его полезной работой. Фактически, async просто предоставляет удобный интерфейс для формирования APC очереди. Просто помимо проблемы 10000 потоков, я получаю довеском проблему исчерпания пула потоков.

А чем поток внутри занимается? Какова нагрузка на процессор?
Если он, что то ожидает то твой то нужно заменить все синхронные операции на асинхронные.
Thread.Sleep заменяется на await Task.Delay().
и солнце б утром не вставало, когда бы не было меня
Отредактировано 05.08.2016 17:23 Serginio1 . Предыдущая версия .
Re[9]: Как оптимизировать выполнения 10000 параллельных зада
От: Sharov Россия  
Дата: 05.08.16 17:11
Оценка:
Здравствуйте, LWhisper, Вы писали:

S>>Что призван продемонстрировать этот код? Зачем в lock Console.Readline()?

S>>С чего это вдруг? Есть такое понятие как логическая блокировка -- задача висит до выполнения условия, а поток возвращается в пул.
LW>Просто вечная блокировка.
LW>Скриншот выше демонстрирует обратное. Поток не возвращается в пул, а висит в ожидании блокировки. Было запущено 30 задач — 30 потоков из пула сели на lock и остались в таком состяонии. Вершины стеков в нижней части скриншота.

Очевидно, что рукотворная вечная блокировка потоки в пул не вернет. Асинхронное IO потоки возвращает, ибо блокировка логическая. Т.е. 10000 соединений проблемой не должны быть.
Кодом людям нужно помогать!
Re[10]: Как оптимизировать выполнения 10000 параллельных зад
От: LWhisper  
Дата: 06.08.16 01:34
Оценка:
Здравствуйте, Sharov, Вы писали:

S>Очевидно, что рукотворная вечная блокировка потоки в пул не вернет. Асинхронное IO потоки возвращает, ибо блокировка логическая. Т.е. 10000 соединений проблемой не должны быть.

Стоп. Сейчас речь о конкретном решении, предложенном в данной ветке — пакете AsyncEx и реализации AsyncManualResetEvent в нём. Судя по тому, что я вижу — там самая что ни на есть полноценная блокировка, удерживающая поток. Соответственно ни о каком "True Async" не может быть и речи.

Опять же — если вас не затруднит, приведите, пожалуйста, пример логической блокировки, которая возвращает поток в пул, а, после захвата блокировки, продолжает выполнение в новом потоке.

UPD:
На всякий случай переписал создание новой таски с использованием async delegate. Как и ожидалось, это ничего не изменило — несколько тысяч тредов, повисших на блокировке.
Task.Run(async delegate
{
    await Task.Delay(100);
    Console.WriteLine(Thread.CurrentThread.ManagedThreadId);
    lock (_lock)
    {
    }
    await Task.Delay(100);
});
Отредактировано 06.08.2016 1:47 LWhisper . Предыдущая версия .
Re[8]: Как оптимизировать выполнения 10000 параллельных зада
От: LWhisper  
Дата: 06.08.16 01:49
Оценка:
Здравствуйте, Serginio1, Вы писали:

S> А чем поток внутри занимается? Какова нагрузка на процессор?

S> Если он, что то ожидает то твой то нужно заменить все синхронные операции на асинхронные.
S> Thread.Sleep заменяется на await Task.Delay().
В лучшем случае — по стабильному каналу общаются с SQL-сервером.
В худшем — по крайне не стабильному с сервером на другом конце планеты, постоянно устанавливая соединение заново, когда жирафы прерывают луч лазерного интернета.
Re[9]: Как оптимизировать выполнения 10000 параллельных зада
От: Serginio1 СССР https://habrahabr.ru/users/serginio1/topics/
Дата: 06.08.16 05:55
Оценка:
Здравствуйте, LWhisper, Вы писали:

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


S>> А чем поток внутри занимается? Какова нагрузка на процессор?

S>> Если он, что то ожидает то твой то нужно заменить все синхронные операции на асинхронные.
S>> Thread.Sleep заменяется на await Task.Delay().
LW>В лучшем случае — по стабильному каналу общаются с SQL-сервером.
LW>В худшем — по крайне не стабильному с сервером на другом конце планеты, постоянно устанавливая соединение заново, когда жирафы прерывают луч лазерного интернета.

Для запросов используй асинхронные методы например Async Query &amp; Save (EF6 onwards)

Основное отличие задач от потоков заключается в сохранении состояния. Для каждого потока выделяется память под стек плюс данные под переменные потока и значения регистров. В задаче это данные объекта (Замыкание). У них свой планировщик.
Только потоки жрут значительно больше ресурсов. Правда при асинхронном программировании нужно использовать асинхронные методы

https://msdn.microsoft.com/ru-ru/library/5cykbwz4(v=vs.110).aspx

Начиная с версии .NET Framework 4, только полностью доверенный код может установить maxStackSize значение больше, чем размер стека по умолчанию (1 МБ). Если указано большее значение для maxStackSize при выполнении кода с частичным доверием maxStackSize игнорируется и используется размер стека по умолчанию. Исключение не возникает. Код на любом уровне доверия, может установить maxStackSize значение меньше, чем размер стека по умолчанию.

Если maxStackSize меньше, чем минимальный размер стека, используется минимальный размер стека. Если maxStackSize не кратному размеру страницы, он округляется до следующего большего кратной размеру страницы. Например при использовании .NET Framework версии 2.0 в Windows Vista, 256 КБ (262 144 байт) является минимальным размером стека, а размер страницы равен 64 КБ (65 536 байт).

и солнце б утром не вставало, когда бы не было меня
Отредактировано 06.08.2016 8:54 Serginio1 . Предыдущая версия . Еще …
Отредактировано 06.08.2016 6:10 Serginio1 . Предыдущая версия .
Re[5]: Как оптимизировать выполнения 10000 параллельных зада
От: Слава  
Дата: 06.08.16 07:01
Оценка:
Здравствуйте, LWhisper, Вы писали:

LW>Зачем — это вопрос к пользователям. Они купили себе 100 слотов, и имеют право использовать их по своему усмотрению. Моя задача — обеспечить стабильную работу сервера в такой ситуации.


А это уже называется оверселл, оно же "купить на грош пятаков". Это проблема руководства, а не ваша, пусть-ка докупают оборудования.
Отредактировано 06.08.2016 7:10 Слава . Предыдущая версия .
Re[6]: Как оптимизировать выполнения 10000 параллельных зада
От: LWhisper  
Дата: 08.08.16 09:50
Оценка:
Здравствуйте, Слава, Вы писали:

С>А это уже называется оверселл, оно же "купить на грош пятаков". Это проблема руководства, а не ваша, пусть-ка докупают оборудования.

Это наша проблема. В частности — проблема архитектуры. Если бы у нас была возможность горизонтального масштабирования, мы бы могли посоветовать именно это. Но такой возможности нет, а по вертикали выше головы не прыгнешь.
Re[10]: Как оптимизировать выполнения 10000 параллельных зада
От: LWhisper  
Дата: 08.08.16 09:53
Оценка:
Здравствуйте, Serginio1, Вы писали:

S>https://msdn.microsoft.com/ru-ru/library/5cykbwz4(v=vs.110).aspx

S>

S>Начиная с версии .NET Framework 4, только полностью доверенный код может установить maxStackSize значение больше, чем размер стека по умолчанию (1 МБ). Если указано большее значение для maxStackSize при выполнении кода с частичным доверием maxStackSize игнорируется и используется размер стека по умолчанию. Исключение не возникает. Код на любом уровне доверия, может установить maxStackSize значение меньше, чем размер стека по умолчанию.

S>Если maxStackSize меньше, чем минимальный размер стека, используется минимальный размер стека. Если maxStackSize не кратному размеру страницы, он округляется до следующего большего кратной размеру страницы. Например при использовании .NET Framework версии 2.0 в Windows Vista, 256 КБ (262 144 байт) является минимальным размером стека, а размер страницы равен 64 КБ (65 536 байт).

Как видно из примеров выше, это не решение всех проблем.
Так или иначе, в коде есть злачные закоулки с локами, мьютексами и вейт хэндлами. Вместо потоков на них будут виснуть таски.
Как этого избежать, переписав всё — понятно. Как обойтись малой кровью — увы, нет.
Re[11]: Как оптимизировать выполнения 10000 параллельных зад
От: Serginio1 СССР https://habrahabr.ru/users/serginio1/topics/
Дата: 08.08.16 10:13
Оценка: 6 (1)
Здравствуйте, LWhisper, Вы писали:


LW>Как видно из примеров выше, это не решение всех проблем.

LW>Так или иначе, в коде есть злачные закоулки с локами, мьютексами и вейт хэндлами. Вместо потоков на них будут виснуть таски.
LW>Как этого избежать, переписав всё — понятно. Как обойтись малой кровью — увы, нет.

Ны дык все их нужно заменять на аналоги
Microsoft.VisualStudio.Threading

AsyncReaderWriterLock
AsyncAutoResetEvent()

И только там , где это невозможно использовать потоки. При этом получится гибридная система, но с минимумом ручных потоков.
и солнце б утром не вставало, когда бы не было меня
Отредактировано 08.08.2016 10:14 Serginio1 . Предыдущая версия .
Re[11]: Как оптимизировать выполнения 10000 параллельных зад
От: Sharov Россия  
Дата: 08.08.16 10:22
Оценка:
Здравствуйте, LWhisper, Вы писали:

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


S>>Очевидно, что рукотворная вечная блокировка потоки в пул не вернет. Асинхронное IO потоки возвращает, ибо блокировка логическая. Т.е. 10000 соединений проблемой не должны быть.

LW>Стоп. Сейчас речь о конкретном решении, предложенном в данной ветке — пакете AsyncEx и реализации AsyncManualResetEvent в нём. Судя по тому, что я вижу — там самая что ни на есть полноценная блокировка, удерживающая поток. Соответственно ни о каком "True Async" не может быть и речи.

Не работал с этой библиотекой и этим примитвом. Поверхостный просмотр github'а выдал такой комментарий для метода wait (что логично):

Synchronously waits for this event to be set. This method may block the calling thread.


Больше про блокировки упоминаний нет.

LW>Опять же — если вас не затруднит, приведите, пожалуйста, пример логической блокировки, которая возвращает поток в пул, а, после захвата блокировки, продолжает выполнение в новом потоке.


Вроде бы WhenAll (ContinueWhenAll), который появился в версии фреймворка 4.5. Я использую библиотеку ParallelExtensionsExtras, который дополняет функциональность 4.0.
Кодом людям нужно помогать!
Re[7]: Как оптимизировать выполнения 10000 параллельных зада
От: Слава  
Дата: 08.08.16 10:57
Оценка:
Здравствуйте, LWhisper, Вы писали:

С>>А это уже называется оверселл, оно же "купить на грош пятаков". Это проблема руководства, а не ваша, пусть-ка докупают оборудования.

LW>Это наша проблема. В частности — проблема архитектуры. Если бы у нас была возможность горизонтального масштабирования, мы бы могли посоветовать именно это. Но такой возможности нет, а по вертикали выше головы не прыгнешь.

Еще раз перечитал тред.

1) 10000 потоков происходят от того, что активны одновременно 100 пользователей и у каждого по 100 потоков? Если так, то пересекаются ли разные пользователи друг с другом, или у каждого пользователя свои данные, с которыми и работают потоки? Если не пересекаются, то почему нельзя раскидывать пользователей по разным машинам? Вот и горизонтальное масштабирование.

2) Из начала треда возникает ощущение, что у вас там дедлоки. Какая-то закольцованная очередь ожиданий друг друга. Если это так, то никакое масштабирование не поможет, это просто баг. Deadlock detection? Postsharp что-то предоставляют по борьбе с дедлоками: http://www.postsharp.net/blog/post/Deadlock-Detection-using-PostSharp-Threading-Toolkit

3) О вертикальном масштабировании — где-то я видел впечатляющий скрин виндов, запущенных на IBM Power. Там можно запускать больше физических потоков, чем на любом x86 сервере. Это очень дорого, но я же не знаю, сколько денег есть у вас.
Re[8]: Как оптимизировать выполнения 10000 параллельных зада
От: LWhisper  
Дата: 08.08.16 15:35
Оценка:
С>1) 10000 потоков происходят от того, что активны одновременно 100 пользователей и у каждого по 100 потоков? Если так, то пересекаются ли разные пользователи друг с другом, или у каждого пользователя свои данные, с которыми и работают потоки? Если не пересекаются, то почему нельзя раскидывать пользователей по разным машинам? Вот и горизонтальное масштабирование.
Пересекаются.

С>2) Из начала треда возникает ощущение, что у вас там дедлоки. Какая-то закольцованная очередь ожиданий друг друга. Если это так, то никакое масштабирование не поможет, это просто баг. Deadlock detection? Postsharp что-то предоставляют по борьбе с дедлоками: http://www.postsharp.net/blog/post/Deadlock-Detection-using-PostSharp-Threading-Toolkit

Дедлок происхоидт из-за того, что задачи, которые должны освободить ресурсы, запускаются асинхронно, но так и висят в очереди на запуск. До кучи встаёт колом очередь финализации сборщика мусора, которая также блокирует запуск новых потоков шедулером.

С>3) О вертикальном масштабировании — где-то я видел впечатляющий скрин виндов, запущенных на IBM Power. Там можно запускать больше физических потоков, чем на любом x86 сервере. Это очень дорого, но я же не знаю, сколько денег есть у вас.

Поскольку железо вместе с софтом мы не поставляем, то такой сценарий не рассматривается. Предлагать подобный апгрейд кастомерам также бессмысленно.
Подождите ...
Wait...
Пока на собственное сообщение не было ответов, его можно удалить.