Delphi 6/7 - какую книгу прочитать про потоки и их допустимое колличество
Требуется быстро поднять знания по достаточно не новой среде Delphi 6 или 7, в общем то я с ней работал, но вот нужно глубже понимать работу с потоками.
Не совсем понятно в каких случаях нужно применять Synchronize, вернее с при работе с чем, а с чем нет.
Так же вызов Terminate он ведь только параметр объекту передаёт, но ведь не останавливает работу потока, верно? Или останавливает всё же? Я так понял что проверка внутри процедуры потока пишется на параметр
Terminatedили не обязательно?И пожалуй основной момент, ну а как под современные ПК рассчитать оптимальное количество потоков и приритетов для них, ведь ПК уже не те что в начале 2000-х годов? Может есть какая то методика.
Другая Delphi не нужна, часть модулей уже есть, и в общем-то то, что планировали доработать уже почти готово - вопрос в том что по-моему комп прога подвешивает.
В общем-то пишем бота (вернее дописываем), там уже и распознавание образов на экране и с голосом фишки, всё это по потокам надо разбить.
Ответы (2 шт):
Synchronize используется для работы с VCL компонентами, также предотвращает одновременный доступ нескольких потоков к 1 Компоненту.
Terminate завершает поток, освобождая ресурсы и по окончании передает свойство true Terminated. Если вам нужно только выполнить код и завершить поток, используйте FreeOnTerminate, для более серьезных задач, когда потоку надо выполнятся до определенного времени тогда используйте Terminate
По расчету потоков тут все индивидуально, все зависит от сложности задачи, если тупо в программе нет ограничения по кол. потоков, то мониторим те ресурсы которые используют потоки, нагрузку на ЦП, память, диск и т.д
Synchronize - используется для выполнения кода в UI-потоке приложения.
Когда использовать Synchronize:
- если нужно обратиться к компоненту пользовательского интерфейса: обязательно, когда записываете данные (
Label1.Caption := 'Text'), но частенько и при чтении тоже (зависит от конкретных компонентов и тех данных, что из них достаются); в целом, следует предполагать по умолчанию, что все компоненты пользовательского интерфейса не поддерживают многопоточного доступа; - если нужно обращаться к некоторому объекту/переменной, которая не поддерживает безопасный многопоточный доступ: в этом случае все операции чтения и записи вы "складываете" в UI-поток (не оптимально, но - допустимо).
- если нужно обратиться к компоненту пользовательского интерфейса: обязательно, когда записываете данные (
Terminate - тут Вы правильно поняли: просто выставляется переменная
FTerminated := True.Если Вы изучали C#, то там есть
CancellationToken. Здесь, в Delphi, принцип похожий - просигнализировать коду потока, что надо завершиться досрочно.Чтобы метод
Terminateработал, код потока должен в ходе выполнения периодически проверять членFTerminated, и если он равенTrue- досрочно завершать работу. Такое поведение легко реализуется, если у вас цикл или какая-то пошаговая работа (проверяете после каждой итерации или шага наFTerminated, и еслиTrue- то делаетеExitв методеTMyOwnThread.Execute();).Следует иметь в виду, что принудительное завершение потоков API-функцией
TerminateThreadсчитается нежелательной практикой (могут возникать проблемы, например утечка памяти), и его рекомендуется избегать. ИспользованиеTerminate/FTerminatedтут как раз может помочь, хотя программист вправе реализовывать и свои способы подобной "сигнализации".По числу потоков - вопрос сложный. Всё сильно зависит от конкретных решаемых задач.
Когда выполняете длительную параллельную обработку данных, то скорей всего стоит ограничиться числом видимых в системе процессорных ядер. Если захотите гарантировать отсутствие подвисаний UI, можете поиграть с affinity-масками потоков, чтобы ни один из рабочих потоков не выполнялся на одном ядре с потоком UI. В Delphi 6/7 это не реализовано, поэтому придётся идти на поклон к Win32 API: SetThreadAffinityMask
Когда выполняете мелкие задачи, отрабатывающие быстро, за 1-2 кванта процессорного времени, то можете запускать хоть сотни таких потоков за один присест. Будут накладные расходы на создание и уничтожение потоков системой, но до определённого порога это не будет заметно.
Если же мелкие задачи выполняете тысячами и десятками тысяч, то вместо создания нового потока под каждую задачу - лучше реализовать свой пул потоков. Навроде того, как сделаны
Taskв .NET. Если память не подводит, в .NET дефолтный пул использует количество потоков в 2 раза больше, чем имеется в системе процессорных ядер, но там имеется документированная рекомендация к программистам - выполнять в пуле только "короткие" (по времени ЦПУ) задачи.