TThreadFunc — Тип Delphi

Delphi Threading

Мне нужно запустить поток в Delphi и использовать для этого следующий код:

Когда я запускаю RunThread, файл журнала содержит записи

но нет выхода журнала из ThreadFunc .

Как я должен изменить этот код для того, чтобы выполнялась реальная функция потока ( ThreadFunc )?

Почему бы не использовать TThread? Делает это довольно легко реализовать темы. ;-)

На OutputDebugString

Проблема в вашем случае, вероятно, связана с OutputDebugString и с тем, как Delphi >

Чтобы проверить, правильно ли Delphi обрабатывает OutputDebugString, используйте альтернативное приложение для отображения сообщений отладки.

Выйдите из Delphi, получите DebugView от Windows Sysinternals по адресу https://technet.microsoft.com/en-us/sysinternals/debugview.aspx , запустите его, и пока он работает, также запустите ваше приложение (просто убедитесь, что Delphi не Бег).

В журнале DebugView вы увидите что-то вроде этого:

OutputDebugString слишком неясен, чтобы полагаться на него. Просто более простые методы, чтобы убедиться, что ваш поток работает, например, создать файл или вызвать звуковой сигнал. Итак, идея в том, что если DebugView не помогает, просто OutputDebugString из вашего потока и использование другого метода гарантируют, что поток работает. Например, добавьте следующую строку в начало вашей ThreadFunc:

Запустите ваше приложение. Если вы увидите диалоговое окно сообщения, значит ваш поток работает нормально.

На конвенции о вызовах

Соглашение о вызовах (stdcall против регистра) здесь не является проблемой. Ваш код верен в этом смысле. Посмотрите на реализацию функции BeginThread в вашем System.pas (и для определения TThreadFunc, если она существует).

Вы не упомянули, какую версию Delphi вы использовали, или, возможно, вы используете другой совместимый компилятор — это могло повлиять на типы, но вероятность этого крайне мала. Delphi 10.2 использует функцию ThreadWrapper для передачи ее в Windows API CreateThread (). Этот ThreadWrapper определен с соглашением о вызовах «stdcall», как того требует спецификация Win32, поскольку он напрямую передается в CreateThread (), который выполняется. ThreadWrapper, в свою очередь, вызывает вас TThreadFunc, который имеет соглашение о вызовах регистра. TThreadFunc определяется следующим образом (простым и понятным):

Итак, как показал приведенный выше код, ваш ThreadFunc должен точно соответствовать объявлению TThreadFunc. Это не обязательно должен быть «стандартный вызов», как говорят некоторые люди в комментариях. Если ваша версия Delphi не имеет TThreadFunc в System.pas, найдите тип, необходимый для третьего параметра BeginThread, чтобы убедиться, что все правильно.

Если у вас включена опция компилятора «Typed @ operator» (которая по умолчанию выключена), и если проблема связана с несоответствием соглашения о вызовах, компилятор выдаст ошибку. Я предлагаю всегда включать опцию «Typed @ operator». Либо добавьте <$ T +>перед вызовом CreateThread (или лучше в начале файла). Таким образом, вы убедитесь, что нет несоответствия.

Как я объяснил, вещи показали, что соглашение о вызовах здесь не является проблемой, и все правильно в вашем коде. Если соглашение о вызовах было бы неуместным, ThreadFunc работал бы в любом случае до конца, поскольку не требует каких-либо параметров до выхода (Dispose (tp)), поэтому возникла бы ошибка нарушения доступа ПОСЛЕ выполнения всех вызовов OutputDebugString — либо на Dispose (tp), либо на выходе из ThreadFunc. Вы бы увидели вывод в отладчике, и ПОСЛЕ ТОГО, ЧТО получил нарушение прав доступа. Поскольку нарушения прав доступа не было — все в порядке с соглашением о вызовах.

Delphi. Потоки (TThread). Создание и синхронизация.

В данной статье разберем простые примеры работы с потоками. Статья основана на многочисленных статьях других блогеров и официальной документации. Разбирался сам, для закрепления написал этот небольшой пост.

Что такое потоки? Зачем нужны потоки и как это работает?

Понятие потоков в Delphi перекочевало из Windows. Очень хорошо об этом написано в книгах Рихтера и Калверта. Но если своими словами и по простому – поток это участок кода, который выполняется параллельно.

Потоки нужны для параллельной работы разных задач приложения. Часто приложение выполняет продолжительные во времени операции. Это как минимум занимает главный поток приложения, который обрабатывает его GUI, скажем. То есть в этот момент нельзя передвинуть форму приложения, нельзя нажать на кнопку. Нельзя практически ничего, пока приложение не закончит начатую задачу.

В Windows работает механизм процессов и потоков. Процесс, согласно Чарльзу Калверту, подобен булеву выражению, он либо есть, либо нет. Потоки же выполняют всю основную работу.

Согласно Чарльзу Калверту это подобно игровому колесу в казино. Допустим оно поделено на 360 частей-секторов. Эти части и есть, образно, потоки. В каждый момент времени какая-то из частей пролетает под стрелкой, которая и укажет в конце выиграли вы или нет. Но выигрыш тут ни при чем. Нас интересует сам механизм. Аналогия в том, что в каждый момент времени одному из потоков выдается квант времени на то, чтобы он выполнил часть задачи. И все это происходит настолько быстро, что человек не успевает замечать. Вот так это работает.

Как создать поток (TThread)?

Теперь посмотрим как это работает в Delphi. Для простоты будем работать с обычными VCL приложениями.

Для начала объявим класс потока в секции type

DelphiComponent.ru — бесплатно видеоуроки по Delphi, статьи, исходники

Типы данных в Delphi, определенные пользователем

Нестандартные типы данных очень часто используются в программирова­нии, поскольку они более информативны, нежели стандартные типы, и по­тому, что их применение ведет к созданию более четкого кода. Это особенно спра­ведливо в отношении записей. Но прежде чем приступать к изучению создания и использования записей, необходимо разобраться в создании и использовании пе­речислимых типов, подтипов и множеств.

Перечислимый тип — это упорядоченный список идентификаторов, который может служить хорошей заменой коллекции констант. Например, следующие кон­станты можно заменить перечислением:

Для объявления перечислимого типа применяется следующий синтаксис:

type

Использование переменной TChessFigure вместо целочисленной переменной делает код более читабельным и позволяет компилятору Delphi осуществлять проверку типа. Каждый идентификатор в перечислении получает порядковое значение. Порядковые значения в перечислении начинаются с 0. Таким обра­зом, Ord(cf (Pawn) равно 0.

Все порядковые процедуры и функции могут работать с перечислимыми типами.

Порядковое значение элемента перечисления можно определить также явно:

Подтип представляет часть базового типа (любого порядкового типа). Для объ­явления подтипа используют тот же синтаксис, что и для объявления массива:

Множества — это коллекции порядковых типов, которые могут содержать до 256 элементов. Чтобы объявить множество, вначале потребуется создать перечис­лимый тип. Затем для объявления множества необходимо воспользоваться сле­дующим синтаксисом:

Множества действительно просты в использовании и позволяют работать с коллекцией значений. Переменная типа множества занимает всего один байт па­мяти. Следующий код иллюстрирует объявление нового множества на основе пе­речисления TDay и присвоение пустого множества переменной ThisWeek:

Операция присваивания позволяет в одном операторе присваивать множеству одно или несколько значений. Чтобы присвоить значения множеству, необходимо заключить их в квадратные скобки. В случае присваивания множеству нескольких значений, они должны разделяться запятыми:

Обычно эти операции служат для добавления или удаления элементов из мно­жества. Однако по возможности вместо них следует применять процедуры Exclude и Include, поскольку процедуры работают быстрее, чем операции:

Зарезервированное слово in можно использовать для определения, присутст­вует ли во множестве указанное значение. Например, для выяснения, содержит ли множество ThisWeek элемент Wednesday, служит приведенный ниже код:

Для просмотра множества можно использовать любой из доступных циклов, в том числе цикл f or-in, применение которого требует наименьшего объема ввода с клавиатуры и ведет к созданию наиболее читабельного кода.

Листинг 7.1. Работа с множествами

Запись — это структурированный тип данных, состоящий из ряда элементов. В отличие от массива, который представляет собой просто группу переменных с общим именем и типом данных, запись — это тип данных, который содержит эле­менты (поля) различных типов. Для создания новой записи необходимо использо­вать зарезервированное слово record и объявить поля внутри блока записи. В действительности объявления полей — это объявления стандартных переменных.

Синтаксис объявления типа записи выглядит следующим образом:

После создания новый тип записи можно использовать для объявления пере­менной этого типа:

Для получения доступа к полям в записи применяется синтаксис с использова­нием точки:

Например, приведенный ниже код заполняет данными запись Book:

Обычно для получения доступа к полям записи необходимо всегда вначале ука­зывать имя записи. Но применение зарезервированного слова with позволяет ука­зывать имя записи только один раз. Это зарезервированное слово служит для оп­ределения блока, в котором можно непосредственно обращаться к полям указан­ной записи.

Синтаксис записи блока with показан ниже:

Блок with позволяет уменьшить объем исходного кода, особенно при использо­вании записи с большим количеством полей или записи с действительно длинным именем.

Следующий пример иллюстрирует определение полей записи в другой записи и использование массивов записей.

Листинг 7.2. Использование записей

При создании процедур и функций, которые принимают запись в качестве па­раметра, этот параметр должен быть объявлен как параметр-переменная или па­раметр-константа. Если поля в записи требуется изменять, следует использовать параметр-переменную. Если необходимо выполнять только считывание значений полей, следует использовать параметр-константу.

При использовании стандартного параметра-значения процедура вынуждена создавать копию всей записи при каждом ее вызове. В данном случае при передаче параметра-значения процедура должна скопировать 92 байта (SizeOf (TBook)). При использовании параметра-константы или параметра-переменной вызов процедуры должен передать только адрес переменной, который занимает всего 4 байта.

Илон Маск рекомендует:  Событие onkeydown в HTML

Работа с потоками в Delphi

Written on 10 Февраля 2009 . Posted in Delphi

Нередко встречал на форумах мнения, что потоки не нужны вообще, любую программу можно написать так, что она будет замечательно работать и без них. Конечно, если не делать ничего серьёзней «Hello World» это так и есть, но если постепенно набирать опыт, рано или поздно любой начинающий программист упрётся в возможности «плоского» кода, возникнет необходимость распараллелить задачи. А некоторые задачи вообще нельзя реализовать без использования потоков, например работа с сокетами, COM-портом, длительное ожидание каких-либо событий, и т.д.

Всем известно, что Windows система многозадачная. Попросту говоря, это означает, что несколько программ могут работать одновременно под управлением ОС. Все мы открывали диспетчер задач и видели список процессов. Процесс — это экземпляр выполняемого приложения. На самом деле сам по себе он ничего не выполняет, он создаётся при запуске приложения, содержит в себе служебную информацию, через которую система с ним работает, так же ему выделяется необходимая память под код и данные. Для того, чтобы программа заработала, в нём создаётся поток. Любой процесс содержит в себе хотя бы один поток, и именно он отвечает за выполнение кода и получает на это процессорное время. Этим и достигается мнимая параллельность работы программ, или, как её еще называют, псевдопараллельность. Почему мнимая? Да потому, что реально процессор в каждый момент времени может выполнять только один участок кода. Windows раздаёт процессорное время всем потокам в системе по очереди, тем самым создаётся впечатление, что они работают одновременно. Реально работающие параллельно потоки могут быть только на машинах с двумя и более процессорами.

Для создания дополнительных потоков в Delphi существует базовый класс TThread, от него мы и будем наследоваться при реализации своих потоков. Для того, чтобы создать «скелет» нового класса, можно выбрать в меню File — New — Thread Object, Delphi создаст новый модуль с заготовкой этого класса. Я же для наглядности опишу его в модуле формы. Как видите, в этой заготовке добавлен один метод — Execute. Именно его нам и нужно переопределить, код внутри него и будет работать в отдельном потоке. И так, попробуем написать пример — запустим в потоке бесконечный цикл:
Запустите пример на выполнение и нажмите кнопку. Вроде ничего не происходит — форма не зависла, реагирует на перемещения. На самом деле это не так — откройте диспетчер задач и вы увидите, что процессор загружен по-полной. Сейчас в процессе вашего приложения работает два потока — один был создан изначально, при запуске приложения. Второй, который так грузит процессор — мы создали по нажатию кнопки. Итак, давайте разберём, что же означает код в Button1Click:
тут мы создали экземпляр класса TNewThread. Конструктор Create имеет всего один параметр — CreateSuspended типа boolean, который указывает, запустить новый поток сразу после создания (если false), или дождаться команды (если true).
свойство FreeOnTerminate определяет, что поток после выполнения автоматически завершится, объект будет уничтожен, и нам не придётся его уничтожать вручную. В нашем примере это не имеет значения, так как сам по себе он никогда не завершится, но понадобится в следующих примерах.
Свойство Priority, если вы еще не догадались из названия, устанавливает приоритет потока. Да да, каждый поток в системе имеет свой приоритет. Если процессорного времени не хватает, система начинает распределять его согласно приоритетам потоков. Свойство Priority может принимать следующие значения:

  • tpTimeCritical — критический
  • tpHighest — очень высокий
  • tpHigher — высокий
  • tpNormal — средний
  • tpLower — низкий
  • tpLowest — очень низкий
  • tpIdle — поток работает во время простоя системы

Ставить высокие приоритеты потокам не стоит, если этого не требует задача, так как это сильно нагружает систему.
Ну и собственно, запуск потока.

Думаю, теперь вам понятно, как создаются потоки. Заметьте, ничего сложного. Но не всё так просто. Казалось бы — пишем любой код внутри метода Execute и всё, а нет, потоки имеют одно неприятное свойство — они ничего не знают друг о друге. И что такого? — спросите вы. А вот что: допустим, вы пытаетесь из другого потока изменить свойство какого-нибудь компонента на форме. Как известно, VCL однопоточна, весь код внутри приложения выполняется последовательно. Допустим, в процессе работы изменились какие-то данные внутри классов VCL, система отбирает время у основного потока, передаёт по кругу остальным потокам и возвращает обратно, при этом выполнение кода продолжается с того места, где приостановилось. Если мы из своего потока что-то меняем, к примеру, на форме, задействуется много механизмов внутри VCL (напомню, выполнение основного потока пока «приостановлено»), соответственно за это время успеют измениться какие-либо данные. И тут вдруг время снова отдаётся основному потоку, он спокойно продолжает своё выполнение, но данные уже изменены! К чему это может привести — предугадать нельзя. Вы можете проверить это тысячу раз, и ничего не произойдёт, а на тысяча первый программа рухнет. И это относится не только к взаимодействию дополнительных потоков с главным, но и к взаимодействию потоков между собой. Писать такие ненадёжные программы конечно нельзя.

Синхронизации потоков

Если вы создали шаблон класса автоматически, то, наверное, заметили комментарий, который дружелюбная Delphi поместила в новый модуль. Он гласит: «Methods and properties of objects in visual components can only be used in a method called using Synchronize». Это значит, что обращение к визуальным компонентам возможно только путём вызова процедуры Synchronize. Давайте рассмотрим пример, но теперь наш поток не будет разогревать процессор впустую, а будет делать что-нибудь полезное, к примеру, прокручивать ProgressBar на форме. В качестве параметра в процедуру Synchronize передаётся метод нашего потока, но сам он передаётся без параметров. Параметры можно передать, добавив поля нужного типа в описание нашего класса. У нас будет одно поле — тот самый прогресс:
Вот теперь ProgressBar двигается, и это вполне безопасно. А безопасно вот почему: процедура Synchronize на время приостанавливает выполнение нашего потока, и передаёт управление главному потоку, т.е. SetProgress выполняется в главном потоке. Это нужно запомнить, потому что некоторые допускают ошибки, выполняя внутри Synchronize длительную работу, при этом, что очевидно, форма зависает на длительное время. Поэтому используйте Synchronize для вывода информации — то самое двигание прогресса, обновления заголовков компонентов и т.д.

Вы наверное заметили, что внутри цикла мы используем процедуру Sleep. В однопоточном приложении Sleep используется редко, а вот в потоках его использовать очень удобно. Пример — бесконечный цикл, пока не выполнится какое-нибудь условие. Если не вставить туда Sleep мы будем просто нагружать систему бесполезной работой.

Надеюсь, вы поняли как работает Synchronize. Но есть еще один довольно удобный способ передать информацию форме — посылка сообщения. Давайте рассмотрим и его. Для этого объявим константу:
В объявление класса формы добавим новый метод, а затем и его реализацию:
Теперь мы немного изменим, можно сказать даже упростим, реализацию метода Execute нашего потока:
Используя функцию SendMessage, мы посылаем окну приложения сообщение, один из параметров которого содержит нужный нам прогресс. Сообщение становится в очередь, и согласно этой очереди будет обработано главным потоком, где и выполнится метод SetProgressPos. Но тут есть один нюанс: SendMessage, как и в случае с Synchronize, приостановит выполнение нашего потока, пока основной поток не обработает сообщение. Если использовать PostMessage этого не произойдёт, наш поток отправит сообщение и продолжит свою работу, а уж когда оно там обработается — неважно. Какую из этих функций использовать — решать вам, всё зависит от задачи.

Вот, в принципе, мы и рассмотрели основные способы работы с компонентами VCL из потоков. А как быть, если в нашей программе не один новый поток, а несколько? И нужно организовать работу с одними и теми же данными? Тут нам на помощь приходят другие способы синхронизации. Один из них мы и рассмотрим. Для его реализации нужно добавить в проект модуль SyncObjs.

Критические секции

Работают они следующим образом: внутри критической секции может работать только один поток, другие ждут его завершения. Чтобы лучше понять, везде приводят сравнение с узкой трубой: представьте, с одной стороны «толпятся» потоки, но в трубу может «пролезть» только один, а когда он «пролезет» — начнёт движение второй, и так по порядку. Еще проще понять это на примере и тем же ProgressBar’ом. Итак, запустите один из примеров, приведённых ранее. Нажмите на кнопку, подождите несколько секунд, а затем нажмите еще раз. Что происходит? ProgressBar начал прыгать. Прыгает потому, что у нас работает не один поток, а два, и каждый из них передаёт разные значения прогресса. Теперь немного переделаем код, в событии onCreate формы создадим критическую секцию:
У TCriticalSection есть два нужных нам метода, Enter и Leave, соответственно вход и выход из неё. Поместим наш код в критическую секцию:
Попробуйте запустить приложение и нажать несколько раз на кнопку, а потом посчитайте, сколько раз пройдёт прогресс. Понятно, в чем суть? Первый раз, нажимая на кнопку, мы создаём поток, он занимает критическую секцию и начинает работу. Нажимаем второй — создаётся второй поток, но критическая секция занята, и он ждёт, пока её не освободит первый. Третий, четвёртый — все пройдут только по-очереди.

Илон Маск рекомендует:  Как в Word выстроить строки в алфавитном порядке

Критические секции удобно использовать при обработке одних и тех же данных (списков, массивов) разными потоками. Поняв, как они работают, вы всегда найдёте им применение.

В этой небольшой статье рассмотрены не все способы синхронизации, есть еще события (TEvent), а так же объекты системы, такие как мьютексы (Mutex), семафоры (Semaphore), но они больше подходят для взаимодействия между приложениями. Остальное, что касается использования класса TThread, вы можете узнать самостоятельно, в help’е всё довольно подробно описано. Цель этой статьи — показать начинающим, что не всё так сложно и страшно, главное разобраться, что есть что. И побольше практики — самое главное опыт!

Delphi многопоточность

Мне нужно, чтобы запустить поток в Delphi и использовать следующий код для этого:

Когда я бегу RunThread, файл журнала содержит записи

но не выход журнала с ThreadFunc .

Как я должен изменить этот код для того , чтобы фактической функции потока ( ThreadFunc ) должны быть выполнены?

Почему бы не использовать TThread? Делает это довольно легко реализовать темы. ;-)

On The OutputDebugString

Проблема в вашем случае, вероятно , с OutputDebugString, и как Delphi >

Для того, чтобы проверить, имеет ли Delphi правильно или нет OutputDebugString, используйте альтернативное приложение для отображения отладочных сообщений.

Выход Delphi, получить DebugView от Windows Sysinternals в https://technet.microsoft.com/en-us/sysinternals/debugview.aspx , запустить его, и в то время как он работает, а также запустить приложение (только убедитесь , что Delphi не Бег).

В журнале DebugView, вы увидите что-то вроде этого:

OutputDebugString слишком неясный, чтобы полагаться на него. Всего более простые методы, чтобы убедиться, что ваш поток выполняется, например, создать файл или вызвать звуковой сигнал. Таким образом, идея заключается в том, если DebugView не помогает, просто OutputDebugString от вашего потока и использовать другой метод действительно убедитесь, что нить работает. Например, добавьте следующую строку в начале вашего ThreadFunc:

Запустите приложение. Если вы увидите диалоговое окно сообщения, то ваш поток работает нормально.

На Вызывающий конвенции

Соглашение о вызовах (STDCALL против регистра) не является проблемой здесь. Ваш код правильный в этом смысле. Посмотрите на реализацию функции BeginThread в вашем System.pas (и для определения TThreadFunc, если она существует).

Вы не упомянули, какую версию Delphi вы использовали, или, возможно, вы используете другой совместимый компилятор — это может повлиять на типы, но вероятность этого крайне мала. Delphi 10.2 использует функцию ThreadWrapper, чтобы передать его в Windows API CreateThread (). Это ThreadWrapper определяется с «STDCALL» соглашение о вызове, в соответствии с требованиями спецификации Win32, так как он непосредственно передается CreateThread (), которая проходит в. ThreadWrapper в свою очередь, призывает вас TThreadFunc который зарегистрировать соглашение о вызовах. TThreadFunc определяется только следующим образом (простой и простой):

Так что, как код выше показал, ваш ThreadFunc должен точно соответствовать декларированию TThreadFunc. Он не должен быть «STDCALL», как некоторые люди говорят, что в комментариях. Если ваша версия Delphi не имеет TThreadFunc в System.pas, обратите внимание на тип необходимого для третьего параметра BeginThread, чтобы убедиться, что все правильно.

Если у вас есть опция компилятора «типизированная @ оператора» включено (который отключен по умолчанию), и если этот вопрос был бы в рассогласованиях вызовов, компилятор дал бы ошибку. Я предлагаю, чтобы всегда проверить опцию «типизированных @ оператора» ON. Кроме того, добавьте <$ T +>до вашего CreateThread вызова (или лучше в начале файла). Таким образом, вы убедитесь, что нет никакого несоответствия.

Как я уже объяснял, все показали, что соглашение о вызовах не является проблемой здесь, и все правильно в вашем коде. Если соглашение о вызовах было бы неуместно, то ThreadFunc не бежал бы в любом случае до конца, так как не нуждается каких-либо параметров до выхода (Dispose (тп)), так что бы была допущена ошибка Нарушение прав доступа после выполнения всех OutputDebugString вызовов — либо на Dispose (TP) или при выходе из ThreadFunc. Вы бы видели выход в отладчик, после чего получил нарушение прав доступа. Поскольку не было нарушением прав доступа — все в порядке с вызывающей конвенцией.

TThreadFunc — Тип Delphi

В этом уроке мы рассмотрим возможность разработки многопоточных приложений в Delphi. Для начала давайте разберем, для чего же нам нужны эти самые потоки, что они из себя представляют.

Для того, чтобы реализовать многозадачность, операционная система выделяет каждому приложению определенное количество процессорного времени, которое она старается распределить по заданным приоритетам. Таким образом производительность каждой такой задачи/потока ограничивается операционной системой в рамках своего процессорного времени. Для того, чтобы выполнять какие-либо действия параллельно (псевдопараллельно), мы можем создавать дополнительные потоки, и в них выполнять те самые действия. Если грамотно распределить какие-либо сложные операции в нашем приложении, то это поможет значительно увеличить производительность ПО.

В модуле Classes в Delphi существует специальный класс TThread, предназначенный для создания потоков. Не забудьте подключить модуль Classes. Рассмотрим создание простого потока. Для этого нам потребуется написать свой класс, который будет наследоваться от класса TThread. Делать мы это будем в разделе type нашего модуля.
[cc lang=»delphi»]type
TMyThread = class(TThread) // Описываем класс нашего нового потока
private
< Private declarations >
protected
procedure Execute; override; // В методе Execute будет находиться непосредственно тело самого потока —
// код, который будет в нем обрабатываться.
end;[/cc]
Теперь установим курсор на наше описание метода Execute, нажмем Ctrl+Shift+C, чтобы перейти к реализации этого метода. Мы увидим следующий код:
[cc lang=»delphi»]procedure TMyThread.Execute;
begin

end;[/cc]
В теле этой процедуры мы и должны будем написать код, который будет обрабатываться в нашем потоке.
Вот таким несложным способом мы можем реализовать наш поток. Осталось только разобраться с тем, как его можно использовать. Создадим экземпляр нашего потока TMyThread:
[cc lang=»delphi»]var MyFirstThread: TMyThread;
begin
MyFirstThread := TMyThread.Create(False);
end;[/cc]
В нашем случае, мы передали конструктору Create нашего потока параметр false. Это означает, что наш поток будет запущен сразу после создания. Если передать True, то поток будет запущен тогда, когда мы вызовем у него метод Resume. Сразу после запуска, поток начнет выполнять программный код метода Execute.
Потокам можно устанавливать их приоритет, от которого будет зависеть количество процессорного времени, которое выделит ОС на данный поток. Другими словами, мы можем настраивать производительность этого потока относительно других. Делается это при помощи свойства Priority у нашего потока:
[cc lang=»delphi»]MyFirstThread.Priority := tpNormal;[/cc]
Существует 7 уровней приоритета для потоков:

  • tpIdle — Самый низкий уровень, поток выполнятся во время «простоя» системы.
  • tpLowest
  • tpLower
  • tpNormal
  • tpHigher
  • tpHighest
  • tpTimeCritical — Самый высокий уровень. Приоритет исполняемого потока равноценен приоритету ядра ОС.

Не переусердствуйте с приоритетами �� Использование tpTimeCritical или tpHighest у нескольких потоков может заставить ваш компьютер «задуматься». Старайтесь использовать tpLower или tpNormal.
Также существует полезное свойство FreeOnTerminate, если установить ему значение True, то поток будет автоматически уничтожен, как только будет завершен программный код метода Execute.
Для выполнения каких-либо циклических операций внутри потока, удобно использовать следующую конструкцию:
[cc lang=»delphi»]procedure TMyThread.Execute;
begin
while true do
begin

end;
end;[/cc]
Содержимое цикла While потока будет обрабатываться, пока внутри цикла не будет вызван break, или пока поток не будет завершен вручную.
Ну, и наконец, уничтожить поток можно методом Terminate:
[cc lang=»delphi»]MyFirstThread.Terminate;[/cc]
Соответственно существует метод Terminated, который возвращает true, если поток был завершен/уничтожен.
Бывает полезно использовать метод Suspend, для того, чтобы временно приостановить выполнение потока, после чего возобновить работу потока можно будет при помощи метода Resume. Чтобы узнать, приостановлен ли поток, можно использовать метод Suspended, который возвращает true, если поток приостановлен.

Одной из самых главных проблем в работе с потоками является невозможность обращаться к каким-либо данным из двух потоков одновременно.
[warning]Вероятнее всего такое одновременное обращение приведет к ошибке Access Violation.[/warning]
Самым типичным примером возникновения такой ошибки является обращение к GUI (к визуальным компонентам формы) из нового потока. Дело заключается в том, что GUI отрисовывается и постоянно обрабатывается в главном потоке вашего приложения, в котором также обрабатывается и другой ваш основной код. Так вот если к GUI мы обратимся еще из какого-то потока, то возникнет ошибка, т.к. GUI уже занят другим потоком. Одним из самых распространенных решений этой проблемы является синхронизация, о которой мы сейчас и поговорим.
Синхронизация позволяет выполнить какой-либо метод в главном потоке приложения, вызвав его при этом в другом потоке.
[warning]Запомните! Все методы, которые вы будете вызывать из тела потока, будут вызываться и обрабатываться в этом потоке. И только для вызова метода в главном потоке, используется синхронизация.[/warning]
Для реализации примера синхронизации создадим метод ChangeGUI, в котором мы будем обращаться к нашему GUI, и соответственно, который будет выполняться в главном потоке, а вызываться из нашего нового. Также добавим в наш поток поле с названием num, в котором мы будем хранить число и постоянно увеличивать его внутри тела потока (для примера).
[cc lang=»delphi»]type
TMyThread = class(TThread) // Описываем класс нашего нового потока
private
< Private declarations >
num: integer;
protected
procedure ChangeGUI; // Метод, в котором мы будем обращаться к GUI, и который будет обрабатываться в главном потоке.
procedure Execute; override; // В методе Execute будет находиться непосредственно тело самого потока —
// код, который будет в нем обрабатываться.
end;

procedure TMyThread.ChangeGUI;
begin
Form2.Label1.Caption := IntToStr(num); // Выводим новое значение num
end;

procedure TMyThread.Execute;
begin
while True do
begin
if num = 1000000 then
num := 0;

Inc(num); // Увеличиваем num на единицу

Илон Маск рекомендует:  Установка скриншотов в каталоге на движке cncat

Synchronize(ChangeGUI); // Та самая синхронизация! Изменяем надпись Label1 в главном потоке
end;
end;

Thread Suspend/Resume

26.06.2020, 17:21

Интересный момент про Thread
Привет всем. Имеется программа с одним потоком созданным через File — New. В потоке прописан.

Поток — Thread, как с ним работать
Здравствуйте, Нужно создать (отдельный модуль) поток(с отображением Progressbar’ом) с одной.

Thread работает параллельно с приложением или НЕТ?
запускаю по ButtonСlk SearchThread.Execute; форма не двигается и выдпет звуковой сигнал при.

Предупреждение W1000 (Функция ‘Resume’ устарела)
begin if Assigned(FOnChange) then begin FThread := TShellChangeThread.Create(FOnChange);.

26.06.2020, 18:24 2

Proper thread synchronization techniques should be based on TEvent and TMutex.

Avoid using Suspend and Resume. Suspending and resuming a thread using these two functions can result in undefined behavior and in deadlock situations that are hard to debug and fix later on.

You can request that a thread end execution prematurely by calling the Terminate method. Terminate sets the thread’s Terminated property to True. If you have implemented the Execute method properly, it checks the Terminated property periodically, and stops execution when Terminated is True.

Слово deprecated дословно означает не «устаревший» (obsolete), а «осуждаемый, не рекомендуемый к использованию», а применительно к Suspend — это предупреждение об опасности неумело-бездумного использования этого метода (типа «прячьте спички от детей» )

Причина этой опасности изложена в хэлпе «ясно», но не совсем точно, т.к. к дедлокам и непредсказуемому поведению может приводить только вызов Syspend из другого потока. Поэтому в мсдн есть ценное замечание, что для избежания неприятностей ф-я SuspendThread должна вызываться только в самом потоке, который нужно приостановить, т.е. основной поток должен лишь устанавливать некоторый флаг, который должен периодически проверяться в рабочем потоке с вызовом Suspend в случае, если этот флаг установлен.

PS: Применительно к TThread эта ситуация полностью аналогична проверке флага Terminated, т.к. вызов ф-ии TerminateThread из другого потока также является deprecated=»осуждаемым», и соотв-но нужно чтобы поток завершался сам по return, если ему извне устанавлен «некий» флаг Terminated

Delphi многопоточность

Мне нужно, чтобы запустить поток в Delphi и использовать следующий код для этого:

Когда я бегу RunThread, файл журнала содержит записи

но не выход журнала с ThreadFunc .

Как я должен изменить этот код для того , чтобы фактической функции потока ( ThreadFunc ) должны быть выполнены?

Почему бы не использовать TThread? Делает это довольно легко реализовать темы. ;-)

On The OutputDebugString

Проблема в вашем случае, вероятно , с OutputDebugString, и как Delphi >

Для того, чтобы проверить, имеет ли Delphi правильно или нет OutputDebugString, используйте альтернативное приложение для отображения отладочных сообщений.

Выход Delphi, получить DebugView от Windows Sysinternals в https://technet.microsoft.com/en-us/sysinternals/debugview.aspx , запустить его, и в то время как он работает, а также запустить приложение (только убедитесь , что Delphi не Бег).

В журнале DebugView, вы увидите что-то вроде этого:

OutputDebugString слишком неясный, чтобы полагаться на него. Всего более простые методы, чтобы убедиться, что ваш поток выполняется, например, создать файл или вызвать звуковой сигнал. Таким образом, идея заключается в том, если DebugView не помогает, просто OutputDebugString от вашего потока и использовать другой метод действительно убедитесь, что нить работает. Например, добавьте следующую строку в начале вашего ThreadFunc:

Запустите приложение. Если вы увидите диалоговое окно сообщения, то ваш поток работает нормально.

На Вызывающий конвенции

Соглашение о вызовах (STDCALL против регистра) не является проблемой здесь. Ваш код правильный в этом смысле. Посмотрите на реализацию функции BeginThread в вашем System.pas (и для определения TThreadFunc, если она существует).

Вы не упомянули, какую версию Delphi вы использовали, или, возможно, вы используете другой совместимый компилятор — это может повлиять на типы, но вероятность этого крайне мала. Delphi 10.2 использует функцию ThreadWrapper, чтобы передать его в Windows API CreateThread (). Это ThreadWrapper определяется с «STDCALL» соглашение о вызове, в соответствии с требованиями спецификации Win32, так как он непосредственно передается CreateThread (), которая проходит в. ThreadWrapper в свою очередь, призывает вас TThreadFunc который зарегистрировать соглашение о вызовах. TThreadFunc определяется только следующим образом (простой и простой):

Так что, как код выше показал, ваш ThreadFunc должен точно соответствовать декларированию TThreadFunc. Он не должен быть «STDCALL», как некоторые люди говорят, что в комментариях. Если ваша версия Delphi не имеет TThreadFunc в System.pas, обратите внимание на тип необходимого для третьего параметра BeginThread, чтобы убедиться, что все правильно.

Если у вас есть опция компилятора «типизированная @ оператора» включено (который отключен по умолчанию), и если этот вопрос был бы в рассогласованиях вызовов, компилятор дал бы ошибку. Я предлагаю, чтобы всегда проверить опцию «типизированных @ оператора» ON. Кроме того, добавьте <$ T +>до вашего CreateThread вызова (или лучше в начале файла). Таким образом, вы убедитесь, что нет никакого несоответствия.

Как я уже объяснял, все показали, что соглашение о вызовах не является проблемой здесь, и все правильно в вашем коде. Если соглашение о вызовах было бы неуместно, то ThreadFunc не бежал бы в любом случае до конца, так как не нуждается каких-либо параметров до выхода (Dispose (тп)), так что бы была допущена ошибка Нарушение прав доступа после выполнения всех OutputDebugString вызовов — либо на Dispose (TP) или при выходе из ThreadFunc. Вы бы видели выход в отладчик, после чего получил нарушение прав доступа. Поскольку не было нарушением прав доступа — все в порядке с вызывающей конвенцией.

TThreadFunc — Тип Delphi

Школа программирования Delphi

Портал DelphiSchool является бесплатным проектом, обеспечивающим пользователям быстрый и легкий доступ к урокам программирования на Delphi. Сайт позволяет научиться программировать на Делфи любому, кто хочеть писать свои программы, игры, Android приложения, программы для MAC OC или IOS. Кроме уроков Delphi и статей Delphi, на сайте доступны также и видеоуроки. Практически к каждому уроку, пользователю доступен исходник, изучив который, он сможет наглядно посмотреть как работает та или иная программа, написанная на Делфи. Кроме того мы постараемся прилагать к каждому материалу (статье, уроку, видеоуроку) файлы Delphi, которые будут помогать изучить предоставленный материал.

Каждый кто решил написать свою программу, создать свое приложение, научиться программировать и т.д., найдет на DelphiSchool статьи Delphi, уроки Delphi, видеоуроки Delphi, исходники Delphi, файлы Delphi, изучит компоненты Delphi, посмотрит Delphi примеры и многое другое абсолютно бесплатно. Все о Delphi программировании теперь собрано на одном сайте.
DelphiSchool

В языке программирования Delphi все должно иметь свой тип. Delphi требует явного указания всех типов. Существует четыре простых типа данных: целые числа, вещественные числа (дробные), строки и булевые значения.

Все переменные должны относиться к какому-то типу. Поскольку переменная — это область памяти, где хранится определенная информация, то для того, чтобы знать, что хранится в этой памяти, мы должны указать компилятору, к какому типу относится переменная. Если переменная относится к типу целых чисел, то в памяти, отведенной под переменную, хранится целое число.

В переменных целого типа, информация представляется в виде чисел, которые не имеют дробной части. Такие числа используются для математических вычислений и любых других операций, в которых требуется работа с числами.

Существует несколько видов целых типов данных. В основном они отличаются только размером отводимой переменным памяти для хранения данных.

В таблице ниже перечислены все типы целых чисел. В примечании указано, какого типа могут быть эти числа — со знаком или без (т. е. только положительные или могут быть и отрицательными). В зависимости от объема памяти, отводимого переменной для хранения данных, определяется максимальное число, которое можно записать в эту переменную.

Таблица. Типы целочисленных переменных

Название

Размер памяти для хранения данных

Как в Delphi использовать исходники на C/C++?

Есть набор исходников на C/C++. Как можно их использовать в Delphi? Может как-то скомпилировать и подключить объектные файлы? Какие есть способы?

2 ответа 2

Так выглядит текст на C++

Обертывание в extern «C» нужно для того, чтобы C++ не выполнял для этой функции name mangling , и в dll она называлась точно также, как у нас в исходнике. Обратите также внимание, что для числовых типов жестко указаны длины в битах — int32_t — чтобы гарантировать совпадение с типами Delphi

Прототип этой функции для Delphi :

Указаны те же параметры, кроме того, указано соглашение о вызовах — cdecl и имя файла dll — showreport.dll и отдельно указано название функции — ShowReport

Полезный инструмент при работе с dll — Dependency Walker

Он выводит список всех функций, которые можно импортировать из DLL , а также список всех ее зависимостей.

Есть 3 варианта:

Cкомпилировать код в виде внешней dll и переписать заголовочники с объявлением экспортируемых функций с Си на Delphi. Обычно, так возможно использовать только Си код, но если сильно захотеть и немало извернуться, то можно и С++: Using C++ objects in Delphi.

Сишным компилятором из поставки RAD Studio, скомпилировать Си код в объектные файлы *.obj , переписать объявление функций и дописать недостающие функции из стандартной библиотеки, которые, возможно использует сишный код. К примеру, именно так в Delphi используется zlib , а в mORMot даже SQLite3 умудрились прилинковать. Но не каждый код получится без проблем так скомпилировать, из-за особенностей компилятора и поддержки стандарта Си. Как это делается: Using C object files in Delphi (пишут, что начиная с XE2 можно даже линковать объектные файлы скомпилированные не в RAD Studio). Про С++, опять же, лучше сразу забыть.

Понравилась статья? Поделиться с друзьями:
Кодинг, CSS и SQL