Synchronize delphi не работает

Synchronize delphi не работает

При использовании в приложении нескольких потоков необходимо гарантировать, что в данный момент только один из потоков может иметь доступ к свойствам и методам объекта VCL — визуального компонента Delphi, то есть действия потоков необходимо синхронизировать между собой. Для выполнения такой синхронизации в Delphi применяется специальный метод Synchronize, в рамках которого и нужно вызывать процедуры, модифицирующие свойства визуальных компонентов.

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

procedure MyThread.Execute;
begin
Synchronize(UpdateCaption);
end;

В данном случае поток используется для изменения заголовка Формы. Изменение заголовка происходит в процедуре UpdateCaption. Казалось бы, для изменения заголовка эту процедуру достаточно вызвать в основной процедуре потока, Execute. Однако, если несколько таких потоков в программе одновременно попытаются изменить заголовок Формы, то это может привести к непредсказуемым последствиям. Для исключения этого процедура UpdateCaption вызывается в процедуре Execute как параметр метода Synchronize.

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

  • во-первых, частый вызов Synchronize тормозит выполнение приложения;
  • во-вторых, если практически все процедуры выполняющегося потока выполняются с использованием метода Synchronize, то смысла в создании такого потока нет — всё равно его работа пройдёт в главном потоке.

Как пример рассмотрим всё ту же модификацию заголовка Формы. Пусть в одном из потоков происходит работа с большим массивом, и требуется отображать какой объём массива уже обработан. Для этого организуем поток, который будет выполнять эту работу. Будем выводить в заголовок Формы индекс элемента, с которым обрабатывающий поток работает в данный момент. Делать это будем с периодичностью 10 раз в секунду. Сначала сделаем так:

Читайте также:  Магнитола kenwood как настроить басы

procedure TMyThread.UpdateCaption;
begin
while True do
begin
Form1.Caption:=IntToStr(I); //I — глобальная переменная основной программы, индекс массива
sleep(100);
end;
end;

procedure TMyThread.Execute;
begin
Synchronize(UpdateCaption);
end;

Видим, что происходит именно то, о чём написано выше. Так как весь код потока, и модификация заголовка Формы, и цикл ожидания, выполняется в методе Synchronize, а значит в главном потоке, то приложение будет выглядеть зависшим, и его даже будет невозможно корректно завершить.
Теперь попробуем вывести цикл за пределы Synchronize:

procedure TMyThread.UpdateCaption;
begin
Form1.Caption:=IntToStr(Cap);
end;

procedure TMyThread.Execute;
begin
while True do
begin
Synchronize(UpdateCaption);
sleep(100);
end;
end;

Это правильный вариант. С помощью метода Synchronize выполняется только непосредственная модификация Заголовка Формы, а цикл ожидания выполняется в потоке, и не мешает главному потоку.

Некоторым объектам VCL процедура Synchronize не требуется, так как они всё же умеют корректно работать с потоками, либо нуждаются в других методах синхронизации. Так, корректно работают с потоками

  • компоненты доступа к базам данных (с использованием компонентов класса TSession). Исключение составляют базы данных Microsoft Access;
  • классы, которые работают непосредственно с графикой. Это TFont, TPen, TBrush, TBitmap, TMetafile и TIcon. Канву объектов этих классов (Canvas) можно использовать не применяя метод Synchronize. Это делается с помощью блокировки канвы. То есть, поток, использующий в данный момент канву, предварительно блокирует канву методом Lock, препятствующим другим потокам работать с канвой, и разблокирует затем методом UnLock.

Кроме визуальных компонентов, также не умеют работать с потоками списки TList. Поэтому в потоках следует использовать объекты TThreadList, которые имеют методы блокировки и разблокировки LockList и UnLockList.

Источник

Synchronize delphi не работает

Такая проблема:
Есть плагин с двумя формами в виде DLL.
Основная программа динамически линкует эту DLL через LoadLibrary по нажатию кнопки, т.е. в основном потоке.
Затем основная программа вызывает экспортированную процедуру этой DLL, createFormProc, которая создает Form1. Тоже в основном потоке.
Owner этой формы — nil.
Юзер перетаскивает файлы из Эксплорера на Form1.
По событию OnDropFiles я создаю Form2(Owner — Form1) в основном потоке и также создаю отдельный тред, передающий файлы по сокету(использую Indy, TIdTcpClient создаю внутри треда).
Все хорошо создается и передается, но при попытке отрисовать ProgressBar на второй форме из треда, где TcpClient, ничего не получается.
Делаю естественно через Synchronize(DoSomeGUIWork), где DoSomeGUIWork — процедура треда.
Проставлял брейкпойнты в этой процедуре — программа туда вообще не заходит.
Практически тот же код прекрасно рисует на форме из треда, если все делается в основной программе. А вот внутри DLL не хочет.

Мне сказали в том смысле, что Synchronize требует активной очереди сообщений для своей корректной работы и что в моем случае таковая отсутствует. Но я же линковал dll и создавал тамошнюю форму в основном потоке приложения.
Еще мне сказали, что в Delphi 6 полностью переработан механизм Synchronize и что у основной программы и у подсоединяемых dll теперь разные TApplication и соответственно разные очереди сообщений.

Подскажите плиз, как заставить Synchronize корректно работать внутри DLL?

Использую Delphi 6 build 6.163


default © ( 2004-06-12 01:28 ) [1]

походу WakeMainThread в nil-е
чтобы её инициал-ать видимо придётся через разд-ую память передать в DLL адрес TApplication.WakeMainThread и всё должно завертеться(это непроверенное на практике. )
DLL вообще же по-моему понятия не имеет о каком-то там Application-е глобальном.


default © ( 2004-06-12 01:30 ) [2]

вообщем исходники, исходники и ещё раз исходники
поверь они расскажут тебе больше чем «Мне сказали в том смысле»


nikkie © ( 2004-06-12 01:48 ) [3]

наверное, надо передать Application в dll и присвоить тамошнему Application.
а вообще забей ты на этот Synchronize. сделай через PostMessage.


default © ( 2004-06-12 01:50 ) [4]

nikkie © (12.06.04 01:48) [3]
а тамошний он есть?

хм. если Forms не подключен, то нет. но в данном случае:

Затем основная программа вызывает экспортированную процедуру этой DLL, createFormProc, которая создает Form1. Тоже в основном потоке.


alienserg ( 2004-06-12 04:33 ) [6]

default © (12.06.04 01:28) [1]
походу WakeMainThread в nil-е
WakeMainThread <> nil
только что проверил.

nikkie © (12.06.04 01:48) [3]
А как с помошью PostMessage сообщить окну адрес треда и адрес процедуры, которую надо исполнить? Я не шибко силен в ручном управлении всей этой механикой. И Рихтера под рукой нет. Поискал в MSDN и не нашел подробного описания структуры сообщений.
Что должно быть в
UINT Msg,
WPARAM wParam,
LPARAM lParam
в моем случае?
Или екзампл какой может у кого найдется.


default © ( 2004-06-12 11:28 ) [7]

alienserg (12.06.04 04:33) [6]
в этой переменной(WakeMainThread)
обычно содержится адрес TApplication.WakeMainThread
попробуй что-нибудь типа
ShowMessage(TApplication(TMethod(WakeMainThread).Data).ExeName)
работает?


nikkie © ( 2004-06-12 16:38 ) [8]

>адрес треда и адрес процедуры
а зачем? тебе просто прогрессбар надо отображать?
перед созданием потока создаешь форму с прогрессбаром.
ее хендл передаешь либо треду в переопределенный конструктор, либо создаешь тред в suspended состоянии и присваиваешь хендл piblic-member-у треда, после чего делаешь resume.
поток по мере того, как что-то происходит, посылает твое сообщение (типа const WM_DOWNLOAD_PROGRESS = WM_USER + 1), передавая в wParam насколько надо прогресс продвинуть. или лучше сколько байт он получил, а форма решает сама, насколько прогресс сдвинуть.


alienserg ( 2004-06-12 16:39 ) [9]

default © (12.06.04 11:28) [7]

TApplication(TMethod(WakeMainThread).Data).ExeName
содержит полное имя хостового приложения вместе с путем.
Посмотрел в DebugMode поскольку ShowMessage из треда не работает.

nikkie © (12.06.04 16:38) [8]

тебе просто прогрессбар надо отображать?
Нет, не только. Мне надо еще отрисовывать процент исполнения в статусе, кол-во байт в статусе, менять заголовки кнопок по ходу дела, менять наименование текущего файла в лейбле, поскольку пересылка пакетная из нескольких файлов. Еще мне надо каждый новый пересылаемый файл добавлять в TListView в ReportMode, чтобы юзер видел историю процесса, успешно ли прошла пересылка, MD5 confirmation отображать и т.д. Мне надо много VCL работы делать.
Попробую испльзовать Delphi Packages вместо стандартных DLL. Может там Synchronize() будет корректно работать.


default © ( 2004-06-12 16:55 ) [11]

alienserg (12.06.04 16:39) [9]
то есть прилож-ия к которому «присоед-ся» DLL?
Application-то получ-ся никакой не DLL-ий(если такой есть вообще. ) и всё вроде бы должно работать.
«
procedure TApplication.WakeMainThread(Sender: TObject);
begin
PostMessage(Handle, WM_NULL, 0, 0);
end;»
попробуй из потока
PostMessage(TApplication(TMethod(WakeMainThread).Data).Handle
, WM_NULL, 0, 0);
или(и) просто Classes.WakeMainThread(nil)
доходит-ли до главного потока WM_NULL?


alienserg ( 2004-06-12 17:13 ) [12]

nikkie © (12.06.04 16:38) [8]

T.e. мне надо поменять стандартный обработчик оконных сообщений?
типа

procedure TForm1.FormCreate(Sender: TObject);
begin
OldWindowProc := self.WindowProc;
self.WindowProc := NewWindowProc;
end;

procedure TForm1.FormDestroy(Sender: TObject);
begin
self.WindowProc := OldWindowProc;
end;

procedure TForm1.NewWindowProc(var Message: TMessage);
begin
if Message.Msg = WM_DOWNLOAD_PROGRESS then DoOnDownloadProgress(Message);
OldWindowProc(Message);
end;


default © ( 2004-06-12 17:18 ) [13]

procedure TForm1.FormCreate(Sender: TObject);
begin
OldWindowProc := WindowProc;
WindowProc := NewWindowProc;
end;

nikkie © (12.06.04 16:38) [8]
«передавая в wParam насколько надо прогресс»

procedure TForm1.NewWindowProc(var Message: TMessage);
begin
if Message.Msg = WM_DOWNLOAD_PROGRESS then DoOnDownloadProgress(Message.WParam) else
OldWindowProc(Message)
end;


default © ( 2004-06-12 18:54 ) [14]

кстати на самом деле есть два Application-а
так что делай замену если что

default © (12.06.04 16:55) [11]

то есть прилож-ия к которому «присоед-ся» DLL?
Именно так.

доходит-ли до главного потока WM_NULL?
А как мне его ловить в главном потоке? Я первый раз этими тропами хожу.
Где у главного потока коллектор-распределитель всех этих сообщений?


default © ( 2004-06-12 19:22 ) [16]

попробуй заменить Application


nikkie © ( 2004-06-12 20:10 ) [17]

проще все.
procedure WMUpdateTraffic(var Message: TMessage); message WM_UPDATE_TRAFFIC;
.

procedure TMainForm.WMUpdateTraffic(var Message: TMessage);
var
BytesReceived: Integer;
begin
BytesReceived := Message.WParam;
FTraffic := FTraffic + BytesReceived;
.
end;


alienserg ( 2004-06-14 01:59 ) [18]

Спасибо всем большое. Очень познавательно было, по крайней мере для меня. Сделал на PostMessage, оказалось не все так сложно, как я себе представлял.
Осталась правда одна непонятная вещь: как только я назначаю какой-либо обработчик для прогресс-окна, созданного внутри dll, в runtime программа начинает верещать «invalid property».
Такое наблюдалось при назначении обработчика OnCreate формы или OnClick. Даже если они были пустыми. Сие непонятно для меня.
Поэтому пришлось переопределять WindowProc формы не в событии OnCreate, а в коде сразу после создания формы.


default © ( 2004-06-14 02:13 ) [19]

alienserg (14.06.04 01:59) [18]
решение [17] это ладно.
для чего два Application-а то вот вопрос.


nikkie © ( 2004-06-14 02:20 ) [20]

вот ёёё.
зачем WindowProc переопределять.
зачем назначать какие-то обработчики в рантайм.
тем более форме, созданной в другом модуле?


alienserg ( 2004-06-14 02:53 ) [21]

nikkie © (14.06.04 02:20) [20]

зачем WindowProc переопределять.
Ну сделал сгоряча 🙂 невнимательно посмотрел на [17].
Работает. Следовательно имеет право на жизнь.
В дальнейшем буду пользоваться [17] приемом 🙂

зачем назначать какие-то обработчики в рантайм.
А почему это в runtime? Я обработчик назначаю в design time.
И все прекрасно компилируется.
В runtime оно только верещит 🙂

Источник

Оцените статью