1
0
mirror of https://github.com/loginov-dmitry/multithread.git synced 2025-02-20 07:58:22 +02:00

Update multithread_in_delphi_for_beginners.md

This commit is contained in:
loginov-dmitry 2021-04-05 23:52:46 +03:00
parent 2ebff421aa
commit b4432227b2

View File

@ -116,7 +116,7 @@ markdown-редакторы не подошли, т.к. они либо не у
12. Работа с базой данных из дополнительного потока [...](#work_with_db)
13. Проблемы, возникающие при создании большого количество потоков [...](#many_thread_problem)
13. Проблемы, возникающие при создании большого количества потоков [...](#many_thread_problem)
# 1. Вступление <a name="intro"></a>
@ -2585,7 +2585,7 @@ end;
5. Уничтожаем объект подключения либо возвращаем его обратно в пул.
# 13. Проблемы, возникающие при создании большого количество потоков <a name="many_thread_problem"></a>
# 13. Проблемы, возникающие при создании большого количества потоков <a name="many_thread_problem"></a>
1. Ограниченность адресного пространства. Сейчас практически никто не использует 32-разрядную ОС Windows. Если мы скомпилируем своё приложение в 32-битном режиме (с размером стека по умолчанию 1 МБ), запустим его в 64-битной ОС Windows и попытаемся создать 2000 потоков, то получим ошибку. Система не в состоянии создать более 1650 потоков. На каждый созданный поток Windows выделяет 1 МБ виртуальной памяти для 32-битного стека и 256 КБ виртуальной памяти для 64-битного стека (его Windows создаёт и поддерживает автоматически для обеспечения возможности работы 32-битных программ в 64-битной ОС). Насколько я понимаю, на размер 64-битного стека мы влиять не можем, но, если уменьшим размер виртуальной памяти под 32-битный стек (это можно сделать в окне Project / Options / Linker / Max stack size), то можем значительно увеличить максимальное количество потоков в приложении. При этом необходимо воздержаться от объявления локальных статических массивов с большим числом элементов. Данная проблема (с ограниченностью адресного пространства) актуальна только для 32-битных приложений. Если вы компилируете 64-битное приложение, то данной проблемы не существует.
@ -2597,6 +2597,12 @@ end;
4. Использование TIdTCPServer для поддержания большого количества сетевых подключений. В древних версиях Indy10 вызовы `Socket.ReadXXX` приводили к высокой загрузке процессора. Приходилось их "разбавлять" с помощью `Sleep(1)` для снижения нагрузки на процессор. Такая же ситуация была с методом `Socket.CheckForDataOnSource`. Более того, раньше метод `Socket.CheckForDataOnSource` ещё и глючил - не возвращал управление, пока не закончится время ожидания, даже если пришли данные (поэтому приходилось его вызывать с минимальным таймаутом). Указанные проблемы приводили к созданию огромной нагрузке на процессор при большом количестве сетевых подключений.
На данный момент в Indy10 эти проблемы решены. Актуальную версию Indy10 можно скачать с github, при этом она обычно работает c любой версией Delphi. Теперь методы `ReadXXX` и `CheckForDataOnSource` не создают никакой нагрузки на процессор и возвращают управление немедленно при появлении данных в сокете (в течение 100 микросекунд, если сервер и клиент запущены на одном компьютере).
5. Большой расход памяти при использовании некоторых менеджеров памяти. Современные высокопроизводительные менеджеры памяти, например, [tcmalloc](https://github.com/google/tcmalloc), могут создавать для каждого потока свой собственный пул блоков памяти. Благодаря этому удаётся минимизировать количество блокировок при выделении и освобождении памяти, что позволяет разрабатывать приложения, которые хорошо масштабируются по количеству ядер, т.е. могут максимально эффективно использовать доступные ресурсы памяти и процессора. Кстати, для Delphi имеется интерфейсный файл [tcmalloc.pas](https://github.com/obones/tcmalloc-delphi), позволяющий использовать менеджер памяти tcmalloc, представленный в виде библиотеки "libtcmalloc.dll". С точки зрения производительности, он значительно эффективнее встроенного в Delphi менеждера памяти, при этом поддерживаются любые версии Delphi, однако в нём отсутствуют многие плюшки, которые есть в FastMM4.
Проблема таких менеджеров памяти заключается в том, что им требуется значительно больший объем памяти ОЗУ по сравнению со стандартным менеджером памяти. На каждый поток такой менеджер памяти запросто может выделить дополнительно по 1 МБ ОЗУ. При использовании такого менеджера памяти вы не сможете создавать тысячи потоков, поскольку память ОЗУ может очень быстро закончиться. С точки зрения разработки серверов это означает, что сетевую библиотеку Indy10 с таким менеджером памяти лучше не использовать. Вместо Indy10 вы можете рассмотреть сетевые библиотеки, работающие по другим принципам, например, использующие асинхронные сокеты Windows (это сложнее, чем Indy10) либо механизм "i/o completion ports" (это значительно сложнее, чем Indy10).
6. Проблема масштабирования стандартного менеджера памяти (и FastMM4). Дело в том, что архитектура менеджера памяти FastMM4 закладывалась ещё во времена 1-ядерных процессоров и 1-поточных программ. Для программы, в которой отсутствуют дополнительные потоки, FastMM4 является оптимальным выбором. В дальнейшем на Delphi стали разрабатывать многопоточные программы, но процессоры всё ещё оставались 1-ядерными. Для многопоточной программы, запущенной на 1-ядерном процессоре, FastMM4 также являлся оптимальным выбором. К сожалению, для многоядерных процессоров FastMM4 не расчитан. Вы не сможете разработать высокопроизводительную программу, которая максимально эффективно утилизирует ядра процессора - узким местом будут обращения к функциям менеджера памяти (GetMem, FreeMem, создание объектов, работа со строками и динамическими массивами и т.д.). Если 2 потока решат одновременно создать объект (TObject.Create), то FastMM4 поставит вызовы функции менеджера памяти в очередь с использованием атомарной функции LockCmpxchg. При этом, если не удалось заблокировать ресурс, то может произойти вызов функции `Sleep(1)`, которая заморозит ожидающий поток до очередного срабатывания системного таймера (примерно на 16 мс).
Хорошая новость заключается в том, что автор библиотеки FastMM4 разработал новый менеджер памяти - [FastMM5](https://github.com/pleriche/FastMM5), который намного лучше масштабируется, однако является платным для коммерческого использования.
<!--
:warning: **Внимание!**
:exclamation: **Внимание!**