|
|
|||||||||||||||||||||||||||||
|
Выполнение кода в потоке без выделения его в процедуруИсточник: delphikingdom Александр Алексеев
Автор: © Александр Алексеев Вашему вниманию (читай: для использования и тестирования) предлагается модуль TasksEx.pas, который предлагает всего две функции:
Код, помещённый между вызовами EnterWorkerThread и LeaveWorkerThread , будет выполняться как если бы он был помещён в метод TThread.Execute . Для начала выполнения кода в другом потоке просто вставьте в код вызов EnterWorkerThread . Для обратного переключения нужно использовать LeaveWorkerThread . Эти вызовы могут быть вложенными, но _обязаны_ быть парными. Также они должны вызываться из одной и той же процедуры/функции/метода. В процедуру LeaveWorkerThread нужно передать результат выполнения функции EnterWorkerThread . Это значение позволяет отличать несколько вызовов EnterWorkerThread друг от друга (поскольку функцию EnterWorkerThread можно вызывать многократно). Вызывающая сторона должна трактовать это значение как простое число и не должна пытаться как-либо интерпретировать его или делать предположение о его содержимом. Всё что она может сделать с ним - передать его в парный вызов LeaveWorkerThread и забыть про него после вызова. Рекомендованная конструкция:
Весь код между EnterWorkerThread и LeaveWorkerThread выполняется во вторичном потоке. Вторичный поток выбирается случайным образом из пула свободных рабочих потоков (используется модуль AsyncCalls , см. ниже). Если таковых нет, то выполнение кода откладывается (добавляется в очередь для исполнения), пока не освободится один из рабочих потоков. Число потоков в пуле контролируется Get/SetMaxAsyncCallThreads . По-умолчанию их не меньше двух. Во время выполнения кода во вторичном потоке или во время ожидания освобождения рабочего потока главный поток находится в цикле вызовов Application.HandleMessage . Во время этого цикла может быть вызван код, вызывающий EnterWorkerThread/LeaveWorkerThread . Поэтому в любой момент времени одновременно может выполняться несколько блоков кода между EnterWorkerThread и LeaveWorkerThread , в том числе и несколько вызовов одного и того же кода. Число одновременно выполняющихся блоков ограничено числом потоков в пуле потоков. В таких случаях выполнение кода после первого LeaveWorkerThread продолжится только после того, как все вложенные вызовы будут завершены. Например (// - главный поток, {} - вторичные потоки ):
Если блок 2 закончит работу раньше, чем блок 1, то ожидания не происходит. Например:
Ситуация аналогична тому, как в однопоточном приложении во время вызова Application.ProcessMessages вызывается длительный обработчик события, и Application.ProcessMessages не возвращает управления, пока обработчик не закончит работу. Эту ситуацию не следует путать с вложенным вызовом EnterWorkerThread :
При выходе из приложения выход откладывается, пока не будут завершены все выполняющиеся блоки вызовов. В коде между EnterWorkerThread и LeaveWorkerThread можно использовать все локальные и глобальные переменные текущей процедуры/функции и её параметры, а также локальные переменные и парметры процедуры/функции, в которую вложена текущая процедура/функция. Исключения, возникшие в этом коде останавливают его выполнение и перевозбуждаются уже в главном потоке. При возникновении исключения LeaveWorkerThread ничего не делает, позволяя завершиться раскрутке исключения. Поэтому:
По этой причине не рекомендуется вставлять код в Finally -блок для LeaveWorkerThread . Интегированный отладчик Delphi не способен следить за выполнением смены потоков. Если вы отлаживаетесь и хотите сделать Step Over для Enter/LeaveWorkerThread , то вы должны поставить breakpoint сразу после вызова этих функций. При повторном вхождении в EnterWorkerThread рабочий поток не обязан быть тем же самым, что и при первом вызове. Например:
Для временного переключения в главный поток используйте функции из AsyncCalls EnterMainThread/LeaveMainThread . Для них справедливы все те же замечания, что и для EnterWorkerThread/LeaveWorkerThread . Например:
Вызов EnterMainThread/LeaveMainThread подобен вызову Synchronize . Поскольку одновременно в главном потоке может выполняться лишь один код, то EnterMainThread блокирует (вызовом EnterCriticalSection ) выполнение потока, если уже есть поток, вызвавший EnterMainThread и не вызвавший ещё LeaveMainThread . Поэтому EnterMainThread/LeaveMainThread не передают контекста, т.к. он сохраняется в глобальной переменной, поскольку в любой момент времени такой контекст может быть только один. Также, во время вызова EnterMainThread/LeaveMainThread рабочий поток ожидает завершения работы блока и не возвращается в пул свободных рабочих потоков. Модуль TaskEx.pas в текущей версии пока не обеспечивает работу с run-time пакетами. Эта возможность в процессе разработки. Скачать пример TasksEx.zip Для работы модуля требуется модуль AsyncCalls (версии 2.0 или выше) от Andreas Hausladen , который можно взять тут: http://andy.jgknet.de/async/ Ссылки по теме
|
|