Здравствуйте, e-Shaman, Вы писали:
ES>Возможно я не впервые поднимаю подобную тему, но дело вот какое... Я заметил один очень удививший меня косяк в Threading-е от .NET Framework 2.0. Мне очень важны ваши, коллеги, комментарии этой ситуации. Далее подробности...
А какие комментарии тут можно сделать? Нужно отдавать себе отчет в том, что Thread как таковой является весьма низкоуровневым средством. Его нужно правильно использовать.
Возможность запускать много потоков одновременно, собственно говоря, нужна для обеспечения рационального использования аппаратуры. Из интуитивных соображений ясно, что приложение с 1 потоком никогда не сможет задействовать несколько CPU одновременно.
Физически, конечно же, никакая машина не сможет реально одновременно исполнять больше потоков, чем в ней CPU.
Однако это вовсе не означает, что нет смысла в создании более чем 2х потоков. Поток — это единица планирования операционной системы. Сама структура вынуждает планировщик быть очень тупым; у него нет практически никакой информации о смысле и цели потоков. Все, что у него есть — это приоритет потока и состояния блокировок.
Работу большинства приложений можно разделить на некие, скажем так, активности (work item). Они могут быть большими и маленькими, могут включать работу с RAM, CPU, HDD, сетью и т.п. Ну, например "обработать клик", "обработать HTTP запрос", "скопировать файл" и т.п. Как я уже говорил, одновременно может выполняться лишь небольшое количество активностей. Все остальные будут вынуждены ждать момента, когда освободятся необходимые ресурсы.
Можно поставить в соответствие 1 активность = 1 поток. Но это довольно дорогостоящее удовольствие; почти что как "одно окно — один монитор". Потому, что
а) даже те потоки, которые спят в ожидании какого-то события, увеличивают объем ынутренних таблиц шедулера, замедляя его процесс принятия решений
б) потоки расходуют системные ресурсы. В первую очередь, каждому потоку выделяется некоторый объем памяти под стек.
Поэтому такой наивный подход применим только для тех случаев, когда активностей в приложении немного и они крупномасштабные.
Для приложений массового обслуживания надо помогать шедулеру. В частности, если заранее известно, что какой-то активности не нужен мегабайт стека, то можно подкрутить настройки потока, резко снизив потребление ресурсов.
Но это только начало. Допустим, мы хотим выполнить 2000 каких-то задачек. При этом мы прикидываем, что одновременно не смогут выполняться более 20. В таком случае, создав для них сразу 2000 потоков, мы вынудим систему отложить нам большое количество ресурсов, которые гарантированно не понадобятся в течение долгого времени.
В свете этого становится ясно, что рассуждения о максимальном количестве ОС потоков, которые можно одновременно запустить, большого смысла не имеют. Не бывает таких приложений, которым нужно 2000 потоков одновременно. 2000 активностей — да. Но только в том случае, если это "ленивые" активности. Например, приложения сетевого обмена. Там все просто: отправив или приняв пачку байт, можно спать бесконечное по меркам CPU время. Тратить целый поток на этот сон разума смысла не имеет. Имеет смысл поставить перед этим потоком новую задачу.
1.2.0 alpha rev. 655
Уйдемте отсюда, Румата! У вас слишком богатые погреба.
Здравствуйте, adontz, Вы писали:
A>Здравствуйте, e-Shaman, Вы писали:
A>Абсолютно идиотский тест. Количество потоков не должно превышать количество физических процессоров больше чем в два раза. Серверный софт с с более чем 4-5 потоками на процессор в топку.
Прежде чем такую чушь пороть посмотри хотя бы сколько поток в процессах Exchange или lsass.
Здравствуйте, adontz, Вы писали:
A>Абсолютно идиотский тест. Количество потоков не должно превышать количество физических процессоров больше чем в два раза. Серверный софт с с более чем 4-5 потоками на процессор в топку.
обоснуйте на чем основаны ваши слова?
На мой взгляд серьезный серверный софт требует достаточно сложных механизмов синхронизации, поэтому обойтись 4-5 потоками будет крайне сложно, разумеется потоки на каждого клиента клепать глупо, но вот держать свой ThreadPool потоки которого обрабатывают клиентов очень удобно.
Спасибо, коллега!
Очень хорошие вещи вы говорите...
Я вот тут уже тоже думаю, что я погорячился с негативом в адрес FX 2.0, и уже думаю, что можно достаточно эффективно заюзать фичу с рулением размером стека, чего не было в FX 1.1. Плюс есть приоритеты, а ещё и собственная очередь ожидания, откладывающая момент реального создания потока, про которую вы говорите.
Так вот я думаю, что если накапливать историю выполнения различных типов активностей, выраженную в каких-то числовых величинах, к которым применимо понятия матожидания, да и вообще какие-нибудь методы прогнозирования, то, опираясь на эти величины, можно управлять параметрами запуска реального потока. При достаточно грамотной реализации это может послужить как раз той самй помощью системе, про которую вы говорите...
Да слова-то, в общем-то, верные. Кол-во активных потоков нужно держать минимальным (пропорционально числу процессоров) и использовать pool. А new Thread() к этому никакого отношения не имеет. Так что тест, основанный на создании огромного кол-ва потоков таким образом, действительно не показательный.
Здравствуйте, Morpheus_, Вы писали:
M_>На мой взгляд серьезный серверный софт требует достаточно сложных механизмов синхронизации, поэтому обойтись 4-5 потоками будет крайне сложно, разумеется потоки на каждого клиента клепать глупо, но вот держать свой ThreadPool потоки которого обрабатывают клиентов очень удобно.
Надо писать statefull finite machine и никаких проблем вида "нехватает потоков" не возникает в принципе. Кстати большинство RFC сетевых протоколов описывают сервер именно в виде конечного автомата оперируя терминами state и command.
Здравствуйте, Morpheus_, Вы писали:
M_>нафик тогда многозадачность нужна, если судя по этим словам самый эффективный сервер это сервер использующий не более 1-го потока...
Текс. Учим матчасть. Переключение потоков не бесплатная операция. Если у тебя более одного реально что-то делающего потока на процессор, то время тратится не только на работу, но и на переключение.
А многозадачность она в целом нужна не между потоками, а между процессами (между потоками внутри процесса можно даже врукопашную переключение сделать), что уже открывает совсем другие возможности.
Здравствуйте, adontz, Вы писали:
A>Текс. Учим матчасть. Переключение потоков не бесплатная операция. Если у тебя более одного реально что-то делающего потока на процессор, то время тратится не только на работу, но и на переключение. A>А многозадачность она в целом нужна не между потоками, а между процессами (между потоками внутри процесса можно даже врукопашную переключение сделать), что уже открывает совсем другие возможности.
Прежде чем рекоммендовать кому-либо учить матчасть, следует ее и самому хотя бы немножко знать. Представь что к серверу поступают два типа запросов: первый тип требует обращения к диску, а второй — чисто вычислительный. К серверу пришел запрос первого типа, который потребует относительно большого времени для ожидания завершения операции с диском. Почему бы это время не использовать с умом и не обслужить запрос второго типа?
Здравствуйте, Lloyd, Вы писали:
L>Прежде чем рекоммендовать кому-либо учить матчасть, следует ее и самому хотя бы немножко знать. Представь что к серверу поступают два типа запросов: первый тип требует обращения к диску, а второй — чисто вычислительный. К серверу пришел запрос первого типа, который потребует относительно большого времени для ожидания завершения операции с диском. Почему бы это время не использовать с умом и не обслужить запрос второго типа?
Вот всё твоё сообщение почему-то исходит из ложной предпосылки первичности синхронного ввода-вывода. На уровне драйверов никто никого не ждёт, используются Completion Handlers в той или иной форме. Так что ты предлагаешь сперва создать систему ожидайния, а потом много потоков, чтобы они пользовались этой системой и тупо ждали. А на самом деле рулит асинхронный ввод-вывод.
Здравствуйте, adontz, Вы писали:
L>>Прежде чем рекоммендовать кому-либо учить матчасть, следует ее и самому хотя бы немножко знать. Представь что к серверу поступают два типа запросов: первый тип требует обращения к диску, а второй — чисто вычислительный. К серверу пришел запрос первого типа, который потребует относительно большого времени для ожидания завершения операции с диском. Почему бы это время не использовать с умом и не обслужить запрос второго типа?
A>Вот всё твоё сообщение почему-то исходит из ложной предпосылки первичности синхронного ввода-вывода. На уровне драйверов никто никого не ждёт, используются Completion Handlers в той или иной форме. Так что ты предлагаешь сперва создать систему ожидайния, а потом много потоков, чтобы они пользовались этой системой и тупо ждали. А на самом деле рулит асинхронный ввод-вывод.
Ну да, рулит. Но путем серьезного увеличения сложности кода.
Здравствуйте, Morpheus_, Вы писали:
M_>Здравствуйте, mrozov, Вы писали:
M>>Кол-во активных потоков нужно держать минимальным (пропорционально числу процессоров)
M_>можно поподробнее откуда появилось это "пропорционально числу процессоров"?
Можно.
Помимо здравого смысла (только один активный поток на процессор имеет смысл, остальные просто ждут своей очереди) есть книга Рихтера о серверном программировании. Очень рекомендую.
M_>нафик тогда многозадачность нужна, если судя по этим словам самый эффективный сервер это сервер использующий не более 1-го потока...
Многозадочность нужно использовать с умом. Сама по себе она ничего не дает, но многое отнимает. Как минимум — ресурсы на каждый поток + время на переключение.
Реальный смысл в многопоточности появляется только тогда, когда потоки часть времени либо спят в ожидании события, либо занимаются вводом/выводом. Но и в том и в другом случае намного эффективнее использовать пул потоков. Причем .net-овский ThreadPool в этом отношении, мягко говоря, не идеален, но все равно лучше, чем создание потоков "с нуля".
В конце концов — процессор физически не может обрабатывать несколько потоков одновременно. HT и мультиядерность в расчет не берем.
Но в любом случае — автор сам признал, что его тест неэффективен. Он просто меряет размер стека, выделяемый потоку по-умолчанию. О чем тут еще говорить-то?
Здравствуйте, adontz, Вы писали:
M_>>нафик тогда многозадачность нужна, если судя по этим словам самый эффективный сервер это сервер использующий не более 1-го потока...
A>Текс. Учим матчасть. Переключение потоков не бесплатная операция.
после того как я написал пару RTOS ядер с гарантированным временем реакции, для меня это не новость
A>Если у тебя более одного реально что-то делающего потока на процессор, то время тратится не только на работу, но и на переключение. A>А многозадачность она в целом нужна не между потоками, а между процессами (между потоками внутри процесса можно даже врукопашную переключение сделать), что уже открывает совсем другие возможности.
и сколько же времени тратится на переключение? А сколько уйдет на проектирование машины состояний? Вы серьезно думаете что код использующий машину состояний легко читать и модифицировать, а следовательно и легко сделать эффективным?
Здравствуйте, adontz, Вы писали:
L>>Ну да, рулит. Но путем серьезного увеличения сложности кода.
A>Ну насчёт серьёзного ты загнул. Всего-то конечный автомат написать.
ну да, а потом чтоб какуюто функцию добавить сидеть разбираться в нем и писать его заново
Здравствуйте, mrozov, Вы писали:
M>>>Кол-во активных потоков нужно держать минимальным (пропорционально числу процессоров)
M_>>можно поподробнее откуда появилось это "пропорционально числу процессоров"? M>Можно. M>Помимо здравого смысла (только один активный поток на процессор имеет смысл, остальные просто ждут своей очереди) есть книга Рихтера о серверном программировании. Очень рекомендую.
речь идет про многоядерные процессоры и HT? или про Z80?
M_>>нафик тогда многозадачность нужна, если судя по этим словам самый эффективный сервер это сервер использующий не более 1-го потока... M>Многозадочность нужно использовать с умом. Сама по себе она ничего не дает, но многое отнимает. Как минимум — ресурсы на каждый поток + время на переключение.
M> M>В конце концов — процессор физически не может обрабатывать несколько потоков одновременно. HT и мультиядерность в расчет не берем.
отчего же так упорно избегать многоядерные процы и процы с HT, сейчас ведь все такие...
M>Но в любом случае — автор сам признал, что его тест неэффективен. Он просто меряет размер стека, выделяемый потоку по-умолчанию. О чем тут еще говорить-то?
речь уже идет не о тесте, а о высказывании "использование на сервере более 1 потока на процессор — ф топку"
Коллега, я что-то не пойму, вы ведете спор ради спора? Я в таких не участвую, мне это не интересно.
HT и мультиядерность в данном контексте просто является аналогом использования нескольких процессоров, вот и все. Я их игнорирую для упрощения терминологии.
Если по существу комментариев нет, то предлагаю на этом дискуссию завершить. Вы задали вопрос "откуда это взялось", я вам ответил, тема по-идее должна быть изчерпана.
P.S. Я-то многопоточные серверные приложения пишу регулярно, работа у меня такая. Рихтер у меня любимый писатель после Чехова. Темой в целом владею.
Здравствуйте, mrozov, Вы писали:
M>Коллега, я что-то не пойму, вы ведете спор ради спора? Я в таких не участвую, мне это не интересно. M>HT и мультиядерность в данном контексте просто является аналогом использования нескольких процессоров, вот и все. Я их игнорирую для упрощения терминологии.
M>Если по существу комментариев нет, то предлагаю на этом дискуссию завершить. Вы задали вопрос "откуда это взялось", я вам ответил, тема по-идее должна быть изчерпана.
Здравствуйте, e-Shaman, Вы писали:
ES>Ну почему же?..
ES>Практический вывод тут как минимум такой — FX 1.1 создаёт и запускает потоки не только быстрее, чем FX 2.0, но и аккуратнее, о чём свидетельствует примерно одинаковое количество максимума этих потоков для разных систем и затрачиваемый при этом объём оперативной памяти.
ES>Поэтому если говорить про стабильность и надёжность работы севрверного решения на основе .NET Framework, то тут всё скорее в пользу старого доброго 1.1.
не убедительный вывод, например фреймворк 1.1 не поддерживает критическую финализацию, что в частности может грозить неприятностями при DoS атаках...