Top.Mail.Ru
Мастера DELPHI, Delphi programming community Титульная страница Поиск, карта сайта Написать письмо 
| Новости | Поиск | FAQ | Статьи | Книги | Новости VCL |
| Форумы ЧАТ | Орешник | KOL и MCK |
Форумы -» Основная («Начинающим»)/ Базы / WinAPI / Компоненты / Сети / Media / Игры / Corba и COM / KOL / FreePascal / .Net / Прочее / RSDN.ORG

« предыдущая ветвь | форум | следующая ветвь »

Синхронизация через SendMessage [D2005, XP]


Страницы:   1 2 3

Alex4445 ©   (2015-12-15 22:37:00) 


type
TMyForm = class (TForm)
...
  procedure SyncMsg (var T : TMessage); message WM_USER + 1;
end

var
  MyForm : TMyForm;

procedure TMyForm.SyncMsg (var T : TMessage);
begin
  CSL.Send ('Enter');   // CodeSiteLogger   - система логгирования
  sleep (2000);
  CSL.Send ('Leave');
end;



TMyThread = class (TThread)
 procedure Execute; override;
end;

procedure TMyThread.Execute;
begin
    while (not Terminated) do
        SendMessage (MyForm.Handle,WM_USER+1,0,0);
        sleep (100);
    end;
end;



Создается несколько однотипных потоков, которые через SendMessage выполняют определенный код (в данному случае лог + слип) в главном потоке.

Ожидаю увидеть в логе:

Enter
Leave
Enter
Leave
...
Enter
Leave
...

Т.е главный поток обрабатывает SendMessage-ы последовательно.

Вижу

Enter
Leave
Enter   // ???
Enter   // ???
Leave
Enter
Leave
...

Как такое получается?  Как начал выполняться последующий SendMesssage пока не завершился предыдущий?
Или это "обман" связанный с работой логгера ?


sniknik   (2015-12-18 14:02:00) [1]

> переполнения стека быть не должно
не было бы, если бы не Application.ProcessMessages; на нем входит в новый цикл обработки в ней читает следующее сообщение очереди, вызывает функцию и т.д. и т.д. фактически получается рекурсия.


Юрий Зотов   (2015-12-18 13:26:00) [2]

ИМХО, при PostMessage переполнения стека быть не должно. Доп. потоки только набрасывают сообщения в очередь главного потока, а главный поток эту свою очередь ЛИНЕЙНО разгребает, одно сообщение за другим. То есть, нет вложенности обработки - значит, нет и переполнения стека.


sniknik   (2015-12-17 17:35:00) [3]

в итоге Rouse в первом же посте был прав. ;)

и кстати PostMessage в этом случае не помогает. ;( а при малых "таймаутах" еще
и стек быстро кончается (обработчики событий один в один вкладываются, как матрешки)


Юрий Зотов   (2015-12-17 16:18:00) [4]

Рассмотрим 4 первых сообщения.

1. Enter 3904:3600 - доп. поток 3600 через SendMessage вызвал обработчик сообщения (это первый вызов) и главный поток 3904 начал этот обработчик выполнять (но еще не закончил).

2. Enter 3904:6700 - в этот момент доп. поток 6700 через SendMessage тоже вызвал обработчик сообщения (это второй вызов) и главный поток 3904 начал этот обработчик выполнять.

3. Leave 3904:6700 - главный поток обработал ВТОРОЙ вызов.

4. Leave 3904:3600  - главный поток обработал ПЕРВЫЙ вызов. Причем вполне успешно - а это означает, что перед п.2 состояние обработчика сообщения было сохранено, а после п.3 - восстановлено.

===============

То есть, имеем вариант 1 в чистом виде (см. [30], [31], [34]). Что и пытаюсь донести.


sniknik   (2015-12-17 15:01:00) [5]

!!!!
> разве что в CodeSiteLogger Send есть что-то подобное - Application.ProcessMessages;
Проверил -
procedure TForm1.SyncMsg (var Message : TMessage);
begin
 Memo1.Lines.Add('Enter '+IntTostr(GetCurrentThreadId)+':'+IntTostr(Message.WParam));
 Application.ProcessMessages;
 Sleep(100);
 Memo1.Lines.Add('Leave '+IntTostr(GetCurrentThreadId)+':'+IntTostr(Message.WParam));
 Application.ProcessMessages;
end;

результат (кусочек, и так очевидно, ну и плюс "жёсткий зависон" исчез(само собой ;), ТС можно по нему ориентироваться, если его код не вешает основной поток то что-то подобное там есть...) -
Enter 3904:3600
Enter 3904:6700
Leave 3904:6700
Leave 3904:3600
Enter 3904:6700
Leave 3904:6700
Leave 3904:6200
Enter 3904:6700
Enter 3904:3600
Leave 3904:3600
Leave 3904:6700
Enter 3904:3600
Leave 3904:3600
Leave 3904:2636
Enter 3904:3936
Enter 3904:6200
Enter 3904:4356
Enter 3904:6700
Enter 3904:3600


sniknik   (2015-12-17 14:53:00) [6]

> хотя "как???" тогда непонятно
разве что в CodeSiteLogger Send есть что-то подобное - Application.ProcessMessages;


sniknik   (2015-12-17 14:44:00) [7]

> либо с Вашим утверждением они не стыкуются
либо ТС вбросил "дезу", у него приведенный код не рабочий, т.е. можно предположить, что "здесь пример,а на самом деле код другой".
[0]
procedure TMyThread.Execute;
begin
   while (not Terminated) do
       SendMessage (MyForm.Handle,WM_USER+1,0,0);
       sleep (100);
    end;
end;


либо реально
Rouse_ ©   (16.12.15 00:03) [1]
Скорее логер, синхронизация на первый взгляд валидная

хотя "как???" тогда непонятно
сделал пример
procedure TMyThread.Execute;
begin
 while (not Terminated) and (not MassStop) do begin
   SendMessage(Form1.Handle, WM_USER+1, GetCurrentThreadId, 0);
   Sleep(Random(100));
 end;
end;

procedure TForm1.SyncMsg (var Message : TMessage);
begin
 Memo1.Lines.Add('Enter '+IntTostr(GetCurrentThreadId)+':'+IntTostr(Message.WParam));
 Sleep(100);
 Memo1.Lines.Add('Leave '+IntTostr(GetCurrentThreadId)+':'+IntTostr(Message.WParam));
end;

правильная последовательность, реальные пары (Message.WParam id потока) ничего не смешивается... хотя добавил Random и поставил меньше ожидание ускорить процесс "на ошибку".

в общем единственное, что осталось против подобного подхода это
> все потоки будут работать последовательно как один, взаимно ожидая друг друга
даже критическая секция для этого не нужна оказалось.
и выйти никак нельзя, клики/кнопки/клавиши все блокирует.


Юрий Зотов   (2015-12-17 13:21:00) [8]

> han_malign ©   (17.12.15 08:43) [34]
> именно вариант 2 является единственным верным...


Тогда откуда взялся сабж? Цитирую:


> Enter
> Leave
> Enter   // ???
> Enter   // ???
> Leave
> Enter
> Leave
> ...

> Как такое получается?   Как начал выполняться последующий
> SendMesssage пока не завершился предыдущий?


Напоминаю, что SendMesssage и обработчик сообщения оба раза выполняются в главном потоке. И он вполне успешно сохраняет текущее состояние первого вызова в момент второго. А после завершения второго так же успешно это состояние восстанавливает.

Либо я неправильно трактую эти факты, либо с Вашим утверждением они не стыкуются. Прошу ответить на вопрос, который я выделил жирным подчеркнутым.


Alex4445   (2015-12-17 12:50:00) [9]


> han_malign ©   (17.12.15 08:43) [34]
> - и тем не менее именно вариант 2 является единственным
> верным...


Согласен,
косвенно нашел этому подтверждение в мануале
"Resources can also be protected using critical sections or the multi-read exclusive-write synchronizer as an alternative to calling Synchronize."

Спасибо всем за участие в обсуждении!


Игорь Шевченко   (2015-12-17 10:30:00) [10]


> А если вызывать его из внешнего процесса?


А если Рихтера почитать ? Поуважать труд популяризаторов.


DayGaykin   (2015-12-17 10:28:00) [11]

SendMessage выполняется в потоке, который его вызывал? Вот это открытие.
А если вызывать его из внешнего процесса?


han_malign   (2015-12-17 08:43:00) [12]


> Вариант 1 мне кажется более реальным

- и тем не менее именно вариант 2является единственным верным...
Последовательность выполнения гарантируется Intel и Microsoft...
У одного потока только один контекст и один стек - выполнить, что то другое (сохранив его состояние)- можно только в контексте другого потока, но не того же самого...
Реентерабельность SendMessage - работает, но только в контексте основного потока - и это честная рекурсия,  ни о каком прерывании выполения речи не идёт...
Если бы был возможен Вариант 1- то никакие примитивы синхронизации не работали бы...
Есть конечно вариант с явным setjmp/longjmp - но это не тот случай...

Поэтому сабжскорее всего - как раз в системе логирования(видимо из-за излишне умной буфферизации)... Если кто не заметил - в сабжеи парность Enter/Leave - не соблюдена...

З.Ы. Я на этом уже не один deadlock словил... Кстати SendMessage - не единственная засада, последний раз я на Visible(aka SetWindowPos, из доп. потока) на сериализацию в основной поток напоролся, который ждал критическую секцию захваченную этим доп. потоком...
З.З.Ы. И не стоит путать TControl.Perform() и SendMessage()...


NoUser   (2015-12-17 00:58:00) [13]

Упс, насчет герц пардон,

а ответ на вопрос "А и Б сидели на трубе ..." - это Ваш   CSL  !


NoUser   (2015-12-17 00:45:00) [14]

> Alex4445   (15.12.15 22:37) [0]
В чем смысл задавать задания с частотой 10Гц, если они будут выполняться с частотой 0,5Гц  ?
- проверить есть ли порог для очереди сообщений?
-- да, он есть, что не влезет - будет отброшено.

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


Юрий Зотов   (2015-12-16 18:48:00) [15]

> Вариант 1 мне кажется более реальным

Собственно, если бы был реальным вариант 2, то не было бы и сабжа.


Юрий Зотов   (2015-12-16 18:40:00) [16]

> han_malign ©   (16.12.15 17:42) [28]
>в D4 - Synchronize работал именно через SendMessage...

Верно. Чем обеспечивалась синхронизация с главным потоком приложения. Но не между несколькими доп. потоками. А сабж именно о них

1. Поток 1 в cвоем Execute вызвал метод M главной формы через Synchronize. Этот метод (для удобства обозначим его M1) начинает выполняться в главном потоке, но до конца еще не выполнился.

2. В этот момент поток 2 в cвоем Execute вызвал тот же самый метод M той же главной формы, и тоже через Synchronize. Этот метод (для удобства обозначим его M2) тоже будет выполнен в главном потоке

Но когда он будет выполнен?

Вариант 1: главный поток временно прервет выполнение метода М1, (сохранив его состояние), затем выполнит метод M2, затем вернется к выполнению метода M1 (напомню, что M1 и M2 - это один и тот же метод главной формы, мы лишь для удобства обозначаем его еще и цифрами).

Вариант 2: главный поток сначала закончит выполнение метода M1, а затем выполнит метод M2.

Вариант 1 мне кажется более реальным (потому что по факту он аналогичен вызову подпрограммы M2 из подпрограммы M1 - то есть, механизм срабатывает автоматически).


sniknik   (2015-12-16 17:57:00) [17]

> если нам безразличен порядок записей в лог (см. [15]).
как раз таки наоборот... порядок будет от порядка очереди сформированной "постом", та же все события с одним приоритетом будут, без очереди не обработаются.


han_malign   (2015-12-16 17:42:00) [18]


> Так мне кажется, по логике,  что 4 не наступит пока не закончится 2.

- правильно кажется...

Если кто забыл, в D4 - Synchronize работал именно через SendMessage...

А в Windows 3.X переключение на другую задачупроисходило в GetMessage, и что то мне подсказывает, что сериализация SendMessage именно там до сих пор и происходит...


Юрий Зотов   (2015-12-16 17:24:00) [19]

> sniknik ©   (16.12.15 15:51) [25]
> нафиг критическую секцию.... ИМХО. PostMessage рулит! :)


Согласен, если нам безразличен порядок записей в лог (см. [15]).

> Alex4445   (16.12.15 15:54) [26]
> нить обработки сообщений не возьмет следующее сообщение
> пока не закончит первое.


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


Alex4445   (2015-12-16 15:54:00) [20]


> 1. Поток 1 посылает сообщение 1 и блокируется системой.
> 2. Главный поток начал (но еще не закончил) обработку сообщения
> 3. Поток 2 посылает сообщение 2 и блокируется системой.
> 4. Главный поток обработал сообщение 2.

Так мне кажется, по логике,  что 4 не наступит пока не закончится 2.
Потому что нить обработки сообщений не возьмет следующее сообщение пока не закончит первое.


Страницы:   1 2 3

Наверх

Титульная страница Поиск, карта сайта Написать письмо