Threads/ru: what changed
Revision 0 to 1. Removed lines have a minus sign; added lines have a plus sign.
Title: Threads/ru
| Old | New | Line | |
|---|---|---|---|
| 1 | + | Free Pascal поддерживает программирование потоков с процедурным и объектно-ориентированным интерфейсом, который в основном не зависит от платформы. | |
| 2 | + | ||
| 3 | + | Официальная документация находится здесь: [Programmer's guide chapter 10](https://www.freepascal.org/docs-html/prog/progch10.html). Оно подробно описывает интерфейс процедурных потоков. | |
| 4 | + | ||
| 5 | + | Объектно-ориентированный интерфейс `TThread` описан более полно на этой странице: [[Multithreaded Application Tutorial/ru|Руководство по разработке многопоточного приложения]] | |
| 6 | + | ||
| 7 | + | ## Заметки по процедурному управлению потоками | |
| 8 | + | ||
| 9 | + | `BeginThread` возвращает дескриптор потока (handle) напрямую, а также возвращает идентификатор потока (ID) в качестве выходного параметра. В системах Posix дескрипторы потоков (handle) отсутствуют, поэтому копия идентификатора потока возвращается и как идентификатор (ID), и как дескриптор(handle). В Windows дескриптор потока (handle) используется для управления всеми потоками, а ID - это отдельное уникальное значение, используемое в качестве перечислителя потоков. Следовательно, независимо от платформы, дескриптор потока, возвращаемый `BeginThread`, соответствует ожиданиям всех других функций потоковой передачи RTL. (`CloseThread`, `SuspendThread`, `WaitForThreadTerminate` и т.д.) | |
| 10 | + | ||
| 11 | + | `GetCurrentThreadID` возвращает конкретно идентификатор потока (ID), а не дескриптор (handle). Таким образом, хотя вы можете использовать это в системах Posix для вызова функций потоковой передачи RTL, в Windows это не сработает. Чтобы получить дескриптор (handle), вы можете использовать `GetCurrentHandle` или `OpenThread` в Windows API или сохранить дескриптор (handle), полученный от `BeginThread`. | |
| 12 | + | ||
| 13 | + | Продолжительность жизни потока состоит из трех этапов: | |
| 14 | + | ||
| 15 | + | - Другой поток создает его, вызывая `BeginThread`. | |
| 16 | + | - Поток завершает свою работу, затем выходит из функции верхнего уровня; или завершает себя явным образом, вызывая `EndThread`; или прибивается другим потоком при вызове `KillThread`. | |
| 17 | + | - Другой поток обнаруживает, что поток завершен, и вызывает `CloseThread` для очистки. | |
| 18 | + | ||
| 19 | + | `CloseThread` ничего не делает в системах Posix, но в Windows освобождает дескриптор потока (handle). Если программа продолжает создавать новые потоки, но не закрывает их, это является утечкой ресурсов и в крайних случаях может привести к нестабильности системы. Все дескрипторы (handle) освобождаются, когда программа завершается, но по-прежнему рекомендуется явно вызывать `CloseThread`, как только поток завершится. Возвращаемое значение `CloseThread` всегда равно 0 в Posix и ненулевое в Windows в случае успеха. | |
| 20 | + | ||
| 21 | + | `DoneThread` и `InitThread` в системном модуле предназначены в первую очередь для внутреннего использования, игнорируйте их. | |
| 22 | + | ||
| 23 | + | `KillThread` следует избегать, если это вообще возможно. Если поток блокирует механизм ручной синхронизации при завершении, любой другой поток, ожидающий реализации этого механизма, может остаться в ожидании навсегда. | |
| 24 | + | ||
| 25 | + | `SuspendThread` и `ResumeThread` также опасны и даже не поддерживаются в системах Posix из-за аналогичных проблем с взаимоблокировкой. Кооперативная синхронизация - безопасная альтернатива (сами потоки периодически проверяют, получают ли они запрос на завершение работы). | |
| 26 | + | ||
| 27 | + | `ThreadGetPriority` и `ThreadSetPriority` позволяют устанавливать приоритет потока от -15 (простоя) до +15 (критический) в Windows. В Posix эти функции еще не реализованы через процедурный интерфейс. | |
| 28 | + | ||
| 29 | + | В модуле system есть функция [GetCPUCount](https://www.freepascal.org/docs-html/rtl/system/getcpucount.html). Используйте ее, чтобы оценить во время выполнения, сколько параллельных потоков нужно создать. | |
| 30 | + | ||
| 31 | + | ## Синхронизация потоков | |
| 32 | + | ||
| 33 | + | Синхронизация используется для обеспечения безопасного доступа различных потоков или процессов к общим данным. Наиболее эффективные механизмы синхронизации предоставляются операционной системой; механизмы синхронизации FPC действуют как минимальная платформенно-нейтральная оболочка для механизмов операционной системы. | |
| 34 | + | ||
| 35 | + | Собственные механизмы синхронизации потоков в модуле system это: критические секции, события RTL, семафоры, `WaitForThreadTerminate`. Их код можно найти в **rtl/\<arch\>/cthreads.pp** или **rtl/\<arch\>/systhrd.inc**. | |
| 36 | + | ||
| 37 | + | Полностью межпроцессное взаимодействие требует более надежного решения, поскольку механизмов синхронизации потоков недостаточно. Для этого может подойти [SimpleIPC](https://www.freepascal.org/docs-html/fcl/simpleipc/index.html). | |
| 38 | + | ||
| 39 | + | ### Критические секции | |
| 40 | + | ||
| 41 | + | Используются функции `InitCriticalSection`, `EnterCriticalSection`, `LeaveCriticalSection` и `DoneCriticalSection`. | |
| 42 | + | ||
| 43 | + | Критические секции - это совместный мьютекс кода, позволяющий только одному потоку одновременно входить в защищенный раздел кода при условии, что каждый поток входит и выходит из раздела чисто. Это безопасный кроссплатформенный способ защиты относительно небольших блоков кода. | |
| 44 | + | ||
| 45 | + | Потоки блокируются из секции только при вызове `EnterCriticalSection`. Если поток каким-то образом может попасть в раздел без вызова `EnterCriticalSection`, он не блокируется и раздел остается небезопасным. Потоки попадают в критическую секцию в порядке [FIFO](https://ru.wikipedia.org/wiki/FIFO). | |
| 46 | + | ||
| 47 | + | Мьютекс критической секции имеет счетчик блокировок. Если поток, содержащий мьютекс критической секции, вызывает `EnterCriticalSection` несколько раз, он должен вызвать `LeaveCriticalSection` равное количество раз, чтобы освободить мьютекс. Если поток выходит из раздела без вызова `LeaveCriticalSection`, например, из-за необработанного исключения, мьютекс остается заблокированным, а другие потоки, ожидающие его, также будут заблокированы. | |
| 48 | + | ||
| 49 | + | Вызов `LeaveCriticalSection`, когда мьютекс не заблокирован, вызывает ошибку в системах Posix, но в Windows он уменьшает счетчик блокировок до значения ниже 0. Вызов `DoneCriticalSection`, пока мьютекс все еще используется, вызывает ошибку в системах Posix, но в Windows он просто блокирует все потоки, ожидающие мьютекса, хотя любой поток, уже находящийся в критической секции, может нормально выйти. | |
| 50 | + | ||
| 51 | + | ### RTLevent | |
| 52 | + | ||
| 53 | + | Функциями используются `RTLEventCreate`, `RTLEventSetEvent`, `RTLEventResetEvent`, `RTLEventWaitFor`, и `RTLEventDestroy`. | |
| 54 | + | ||
| 55 | + | События RTL являются предпочтительным методом межплатформенной синхронизации. События RTL запукаются как неустановленные. Они блокируют потоки, ожидающие их; когда событие установлено, один ожидающий поток освобождается (в порядке [FIFO](https://ru.wikipedia.org/wiki/FIFO)), и событие немедленно сбрасывается. | |
| 56 | + | ||
| 57 | + | Из-за различий в платформах невозможно напрямую запросить текущее состояние события. Также нет способа напрямую определить тайм-аут ожидания события, потому что любое ожидание события вызывает автоматический сброс состояния, а ожидания не имеют возвращаемого значения. | |
| 58 | + | ||
| 59 | + | Если поток начинает ждать события RTL, которое уже было установлено, ожидание завершается немедленно, и событие автоматически сбрасывается. Событие не учитывает, сколько раз оно было установлено, поэтому несколько наборов по-прежнему очищаются одним сбросом. Будьте осторожны: если у вас есть 8 потоков, которые начинают ждать одного события RTL, которое будет установлено 8 раз, потоки могут зайти в тупик, если что-то очистит несколько наборов за один сброс. | |
| 60 | + | ||
| 61 | + | Уничтожение события RTL, пока поток ожидает его, не освобождает поток. Ответственность за то, чтобы ни один поток не ожидал события после его уничтожения, лежит на программисте. | |
| 62 | + | ||
| 63 | + | ### Семафоры | |
| 64 | + | ||
| 65 | + | Семафоры существовали только для Posix и устарели. Они блокируют ожидающие их потоки, и каждый `SemaphorePost` освобождает один ожидающий поток. Хотя семафоры недоступны через RTL, существуют и другие реализации. (где?) | |
| 66 | + | ||
| 67 | + | ### WaitForThreadTerminate | |
| 68 | + | ||
| 69 | + | `WaitForThreadTerminate` блокирует текущий поток до тех пор, пока целевой поток не завершится. Параметр тайм-аута игнорируется на платформах Posix, и время ожидания никогда не истечет. | |
| 70 | + | ||
| 71 | + | Известно, что `WaitForSingleObject` и `WaitForMultipleObjects` не существуют в Linux, поэтому они не подходят для межплатформенного использования. Они могут быть полезны лишь под Windows. | |
| 72 | + | ||
| 73 | + | --- | |
| 74 | + | *From the Free Pascal wiki, page "Threads/ru", as it stood on 2020-08-21 after a change by Zoltanleo, out of the wiki's dump of 2024-05-26. Copyright stays with the author who wrote it, according to the old wiki's [[Licensing]] page. If you have concerns about this material, [message the admin](/wiki-contact).* |
