Главная страница
    Top.Mail.Ru    Яндекс.Метрика
Форум: "Начинающим";
Текущий архив: 2017.11.12;
Скачать: [xml.tar.bz2];

Вниз

Синхронизация через SendMessage   Найти похожие ветки 

 
Alex4445   (2015-12-15 22:37) [0]


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 пока не завершился предыдущий?
Или это "обман" связанный с работой логгера ?


 
Rouse_ ©   (2015-12-16 00:03) [1]

Скорее логер, синхронизация на первый взгляд валидная


 
sniknik ©   (2015-12-16 01:45) [2]

> синхронизация на первый взгляд валидная
вряд ли. SendMessage немедленное выполнение, без очереди, код процедуры не заблокирован, критической секции нет. ничего не мешает второму потоку вызвать свой SendMessage в момент когда первый на sleep-е. sleep в принципе и нужен чтобы "притушить" выполнение текущего, и дать "подышать" другим потокам. вот они и пользуются. это же фактически прямой вызов кода, все одно, что функцию описанную прямо в потоке (или неважно где) вызвать.
поменяй на PostMessage, очередь событий должна исправить ситуацию.
или
+, еще вариант
> Как такое получается?
а посчитай количество Enter-ов и Leave. первых случаем не больше? в смысле может быть приходит "паразитное", забытое сообщение из другого места с тем же кодом?

имхо, для логера нужен отдельный поток + PostThreadMessage, а не основной c событием на форму.


 
Alex4445   (2015-12-16 12:02) [3]


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

где) вызвать.
> поменяй на PostMessage, очередь событий должна исправить
> ситуацию.


Хмм.. как это "без очереди"?  
И Send и Post через очередь сообщений, отличие Send.. от Post.. что первый ждет завершения, а второй - нет.


> а посчитай количество Enter-ов и Leave. первых случаем не
> больше?

Нет. Все как нужно.


 
Rouse_ ©   (2015-12-16 12:10) [4]

А стоп, да затупил - Коля прав, не прочитал про множественные вызовы.
Да, вызов из второй нити приостановит выполнение кода из первой, поэтому в данном случае критсекция нужна


 
Alex4445   (2015-12-16 13:02) [5]



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


Вот такой код должен быть?
Разве сам SendMessage  - не гарантирует синхронизации ???


 
Юрий Зотов ©   (2015-12-16 13:11) [6]

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


А кто ему мешает? Никто. Вот возможный сценарий:

1. Первый поток начал выполнять SendMessage, но еще не закончил (тем более, что в обработчике сообщения стоит Sleep(2000).

2. Второй поток начал выполнять SendMessage, потому что ему никто не мешает.

Нужна критическая секция (например).


 
Alex4445   (2015-12-16 13:12) [7]

На мой взгляд в этом случае получается синхронизация в синхронизации, если при этом через SendMessage еще какие-то данные передаются, то получаем потенциальный deadlock


TMyThread = class (TThread)
 FCS : TCriticalSection;
 FA : Integer;
 procedure Execute; override;
 property  FAA : Integer read GetFA;
end;

procedure TMyThread.GetFA;
begin
  FCS.Enter;
  try
      Result := FA;
  finally  
   FCS.Leave;
 end;
end;

procedure TMyThread.Execute;
begin
   while (not Terminated) do
        FCS.Enter;
         SendMessage (MyForm.Handle,WM_USER+1,FAA,0);   // -- DeadLock
        FCS.Leave;
       sleep (100);
   end;
end;



 
Alex4445   (2015-12-16 13:15) [8]

А кто ему мешает? Никто. Вот возможный сценарий:

1. Первый поток начал выполнять SendMessage, но еще не закончил (тем более, что в обработчике сообщения стоит Sleep(2000).

2. Второй поток начал выполнять SendMessage, потому что ему никто не мешает.

Нужна критическая секция (например).



A... видимо у меня путаница в выполнении ..

Я полагал, что основной поток в очереди сообщений берет SendMessage и "зависает" на нем пока не выполнит. И только после этого берет следующее сообщение SendMessage


 
Alex4445   (2015-12-16 13:16) [9]

По пред. примеру .. видимо тоже должно быть вот так ?


FCSGlobal  : TCriticalSection;

TMyThread = class (TThread)
FCS : TCriticalSection;
FA : Integer;
procedure Execute; override;
property  FAA : Integer read GetFA;
end;

procedure TMyThread.GetFA;
begin
 FCS.Enter;
 try
     Result := FA;
 finally  
  FCS.Leave;
end;
end;

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


 
Юрий Зотов ©   (2015-12-16 13:17) [10]

> Alex4445   (16.12.15 13:02) [5]
> Разве сам SendMessage  - не гарантирует синхронизации ???

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

Причем неважно, что код обработки сообщения написан в классе формы - выполнен этот код будет все равно в потоке, вызвавшем SendMessage.


 
sniknik ©   (2015-12-16 13:19) [11]

> Хмм.. как это "без очереди"?
ну вот так, считай Send просто прямым вызовом функции, и если в одном приложении то аналог -
procedure TMyThread.Execute;
begin
   while (not Terminated) do
       MyForm.SyncMsg(nil); //не буду для примера формировать структуру
       sleep (100);
   end;
end
;
ну без учета vcl/нельзя из потока и т.д.

> Нет. Все как нужно.
ну тогда только первый вариант.

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

просто замени на PostMessage в исходном коде и проверь.


 
Юрий Зотов ©   (2015-12-16 13:21) [12]

Зачем нужна FCS ?


 
Юрий Зотов ©   (2015-12-16 13:30) [13]

> Я полагал, что основной поток в очереди сообщений берет SendMessage
> и "зависает" на нем пока не выполнит

SendMessage - это прямой (т.е. обычный) вызов обработчика, в очередь она ничего не помещает. Вы вызываете SendMessage из НЕглавного потока - вот в этом НЕглавном потоке вся обработка сообщения и выполняется. И, несмотря на то, что обработчик написан в главной форме, выполняется он все равно в НЕглавном потоке (в том, который вызвал SendMessage).

А главный поток здесь ни при чем, он в это время ждет, когда у НЕГО в очереди появится ЕГО сообщение: классический цикл while GetMessage(...)


 
Alex4445   (2015-12-16 13:31) [14]


> выполнен этот код будет все равно в потоке, вызвавшем
> SendMessage.


Юрий, прошу пояснения. ...


procedure TMyThread.Execute;
begin
  while (not Terminated) do
      ...     --- код выполняется в потоке Thread1
      SendMessage (....);  --- вызов выполняется в потоке Thread1
      sleep (100);     ...     --- код выполняется в потоке Thread1
  end;
end;

procedure TMyForm.SyncMsg (var T : TMessage);
begin
 CSL.Send ("Enter");   -- код выполняется в потоке Main (главный поток приложения).
 sleep (2000);          -- код выполняется в потоке Main  
 CSL.Send ("Leave");  -- код выполняется в потоке Main
end;



 
Юрий Зотов ©   (2015-12-16 13:35) [15]

А вообще, если речь идет всего лишь о логировании, то я согласен, что надо использовать PostMessage и не париться. Кстати, и лог получится более информативным (особенно, если потоку дать какое-нибудь имя и выводить это имя в лог вместе с другой инфой).


 
Юрий Зотов ©   (2015-12-16 13:44) [16]

Весь метод TMyForm.SyncMsg будет выполнен в потоке Thread1. С чего бы ему выполняться в потоке Main? То, что этот метод написан в классе формы - неважно.

Thread1 выполняет цепочку вызовов:
- TMyThread.Execute
- SendMessage
- TMyForm.SyncMsg (напрямую вызван из SendMessage)
- sleep (100


А поток Main в этой цепочке вообще не участвует. Он ждет сообщения в СВОЕЙ очереди.


 
sniknik ©   (2015-12-16 13:45) [17]

> код выполняется в потоке Thread/Main
сохрани в лог значение GetCurrentThreadId и посмотри.


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

А если уж очень сильно хочется синхронизации, то выбросить SendMessage, выбросить все эти Sleep"ы, выбросить критическую секцию, выбросить даже и само сообщение. И оставить простейший вариант: писать в лог через Synchronize.

Только тогда пропадает весь смысл второго потока. Поэтому и его надо будет выбрасывать. И останется обычное однопоточное приложение.
:o)


 
Alex4445   (2015-12-16 14:38) [19]

Народ, вопрос никакого отношения к логгированию не имеет.
Для ведения логов используется CodeSiteLogger, входящий в комплект Delphi XE.
Логи были добавлены в обработчик SendMessage, чтобы понять выполняются ли вызовы последовательно один за другим.


unit Unit1;

interface

uses
 Winapi.Windows, Winapi.Messages, System.SysUtils, System.Variants, System.Classes, Vcl.Graphics,
 Vcl.Controls, Vcl.Forms, Vcl.Dialogs, Vcl.StdCtrls;

type
 TMyForm = class(TForm)
   btn1: TButton;
   procedure btn1Click(Sender: TObject);
 private
   { Private declarations }
 public
   { Public declarations }
   procedure SyncMsg (var T: TMessage); message WM_USER + 1;
 end;

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

var
 MyForm: TMyForm;

implementation
USes CodeSiteLogging;
{$R *.dfm}

{ TThread1 }

procedure TThread1.Execute;
begin
 FreeOnTerminate := True;
 CodeSite.Send("Enter Thread Execute ", GetCurrentThreadId);
  SendMessage(MyForm.Handle, WM_USER+1,0,0);
 CodeSite.Send("Done  Thread Execute");
end;

{ TMyForm }

procedure TMyForm.btn1Click(Sender: TObject);
begin
 CodeSite.Send("Button Clicked");
 TThread1.Create;
end;

procedure TMyForm.SyncMsg(var T: TMessage);
begin
 CodeSite.Send("Enter Message");
 sleep (2000);
 CodeSite.Send("Exit Message");
end;

end.


Сообщения логгера

Button Clicked              Thread 1736
Enter Thread Execute     Thread 7656
Enter Message              Thread 1736
Exit Message                 Thread 1736
Done  Thread Execute     Thread 7656


 
Юрий Зотов ©   (2015-12-16 14:48) [20]

Откуда взялись выделенные части лога? В коде их нет.

Button Clicked              Thread 17361736
Enter Thread Execute    Thread 7656
Enter Message              Thread 1736
Exit Message                Thread 1736
Done  Thread Execute   Thread 7656
/CODE>


 
Alex4445   (2015-12-16 14:59) [21]

Это добавляет сам CodeSiteLogger в обработчике Send ();
Собственно, для чистоты эксперимента
CodeSite.Send ()  заменен на OutPutDebugString

utPutDebugString (PChar("Enter Message Thread  " + IntToStr(GetCurrentThreadID)));

Результат:

Debug Output: Button Clicked Thread  5256 Process Project1.exe (1116)
Thread Start: Thread ID: 10136. Process Project1.exe (1116)
Debug Output: Enter Thread Execute Thread  10136 Process Project1.exe (1116)
Debug Output: Enter Message Thread  5256 Process Project1.exe (1116)
Debug Output: Exit Message Thread  5256 Process Project1.exe (1116)
Debug Output: Done  Thread Execute Thread  10136 Process Project1.exe (1116)
Thread Exit: Thread ID: 10136. Process Project1.exe (1116)


 
Alex4445   (2015-12-16 15:02) [22]

Та же вариация самого первого поста с OutPutDebugString  


unit Unit1;

interface

uses
 Winapi.Windows, Winapi.Messages, System.SysUtils, System.Variants, System.Classes, Vcl.Graphics,
 Vcl.Controls, Vcl.Forms, Vcl.Dialogs, Vcl.StdCtrls;

type
 TMyForm = class(TForm)
   btn1: TButton;
   procedure btn1Click(Sender: TObject);
 private
   { Private declarations }
 public
   { Public declarations }
   procedure SyncMsg (var T: TMessage); message WM_USER + 1;
 end;

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

var
 MyForm: TMyForm;

implementation
{$R *.dfm}

{ TThread1 }

procedure TThread1.Execute;
begin
 FreeOnTerminate := True;
// OutPutDebugString (PCHar("Enter Thread Execute Thread  " + IntToStr(GetCurrentThreadID)));
  SendMessage(MyForm.Handle, WM_USER+1,0,0);
//  OutPutDebugString (PCHar("Done  Thread Execute Thread  " + IntToStr(GetCurrentThreadID)));
end;

{ TMyForm }

procedure TMyForm.btn1Click(Sender: TObject);
var
A : Integer;
begin
 OutPutDebugString (PChar("Button Clicked Thread  " + IntToStr(GetCurrentThreadID)));
 for A := 0 to 10 do
   TThread1.Create;
end;

procedure TMyForm.SyncMsg(var T: TMessage);
begin
 OutPutDebugString (PCHar("Enter Message Thread  " + IntToStr(GetCurrentThreadID)));
 sleep (2555);
 OutPutDebugString (PChar("Exit Message Thread  " + IntToStr(GetCurrentThreadID)));
end;

end.


В логе уже отображает ожидаемый изначально результат: последовательный
Enter Message
Exit  Message
...


Debug Output: Button Clicked Thread  3420 Process Project1.exe (8140)
Thread Start: Thread ID: 9960. Process Project1.exe (8140)
Thread Start: Thread ID: 10132. Process Project1.exe (8140)
Thread Start: Thread ID: 8228. Process Project1.exe (8140)
Thread Start: Thread ID: 5324. Process Project1.exe (8140)
Thread Start: Thread ID: 6312. Process Project1.exe (8140)
Thread Start: Thread ID: 9732. Process Project1.exe (8140)
Thread Start: Thread ID: 7696. Process Project1.exe (8140)
Thread Start: Thread ID: 5240. Process Project1.exe (8140)
Thread Start: Thread ID: 9692. Process Project1.exe (8140)
Thread Start: Thread ID: 8916. Process Project1.exe (8140)
Thread Start: Thread ID: 9544. Process Project1.exe (8140)
Debug Output: Enter Message Thread  3420 Process Project1.exe (8140)
Debug Output: Exit Message Thread  3420 Process Project1.exe (8140)
Debug Output: Enter Message Thread  3420 Process Project1.exe (8140)
Thread Exit: Thread ID: 5240. Process Project1.exe (8140)
Debug Output: Exit Message Thread  3420 Process Project1.exe (8140)
Debug Output: Enter Message Thread  3420 Process Project1.exe (8140)
Thread Exit: Thread ID: 10132. Process Project1.exe (8140)
Debug Output: Exit Message Thread  3420 Process Project1.exe (8140)
Debug Output: Enter Message Thread  3420 Process Project1.exe (8140)
Thread Exit: Thread ID: 9960. Process Project1.exe (8140)


 
Alex4445   (2015-12-16 15:02) [23]

Та же вариация самого первого поста с OutPutDebugString  


unit Unit1;

interface

uses
 Winapi.Windows, Winapi.Messages, System.SysUtils, System.Variants, System.Classes, Vcl.Graphics,
 Vcl.Controls, Vcl.Forms, Vcl.Dialogs, Vcl.StdCtrls;

type
 TMyForm = class(TForm)
   btn1: TButton;
   procedure btn1Click(Sender: TObject);
 private
   { Private declarations }
 public
   { Public declarations }
   procedure SyncMsg (var T: TMessage); message WM_USER + 1;
 end;

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

var
 MyForm: TMyForm;

implementation
{$R *.dfm}

{ TThread1 }

procedure TThread1.Execute;
begin
 FreeOnTerminate := True;
// OutPutDebugString (PCHar("Enter Thread Execute Thread  " + IntToStr(GetCurrentThreadID)));
  SendMessage(MyForm.Handle, WM_USER+1,0,0);
//  OutPutDebugString (PCHar("Done  Thread Execute Thread  " + IntToStr(GetCurrentThreadID)));
end;

{ TMyForm }

procedure TMyForm.btn1Click(Sender: TObject);
var
A : Integer;
begin
 OutPutDebugString (PChar("Button Clicked Thread  " + IntToStr(GetCurrentThreadID)));
 for A := 0 to 10 do
   TThread1.Create;
end;

procedure TMyForm.SyncMsg(var T: TMessage);
begin
 OutPutDebugString (PCHar("Enter Message Thread  " + IntToStr(GetCurrentThreadID)));
 sleep (2555);
 OutPutDebugString (PChar("Exit Message Thread  " + IntToStr(GetCurrentThreadID)));
end;

end.


В логе уже отображает ожидаемый изначально результат: последовательный
Enter Message
Exit  Message
...


Debug Output: Button Clicked Thread  3420 Process Project1.exe (8140)
Thread Start: Thread ID: 9960. Process Project1.exe (8140)
Thread Start: Thread ID: 10132. Process Project1.exe (8140)
Thread Start: Thread ID: 8228. Process Project1.exe (8140)
Thread Start: Thread ID: 5324. Process Project1.exe (8140)
Thread Start: Thread ID: 6312. Process Project1.exe (8140)
Thread Start: Thread ID: 9732. Process Project1.exe (8140)
Thread Start: Thread ID: 7696. Process Project1.exe (8140)
Thread Start: Thread ID: 5240. Process Project1.exe (8140)
Thread Start: Thread ID: 9692. Process Project1.exe (8140)
Thread Start: Thread ID: 8916. Process Project1.exe (8140)
Thread Start: Thread ID: 9544. Process Project1.exe (8140)
Debug Output: Enter Message Thread  3420 Process Project1.exe (8140)
Debug Output: Exit Message Thread  3420 Process Project1.exe (8140)
Debug Output: Enter Message Thread  3420 Process Project1.exe (8140)
Thread Exit: Thread ID: 5240. Process Project1.exe (8140)
Debug Output: Exit Message Thread  3420 Process Project1.exe (8140)
Debug Output: Enter Message Thread  3420 Process Project1.exe (8140)
Thread Exit: Thread ID: 10132. Process Project1.exe (8140)
Debug Output: Exit Message Thread  3420 Process Project1.exe (8140)
Debug Output: Enter Message Thread  3420 Process Project1.exe (8140)
Thread Exit: Thread ID: 9960. Process Project1.exe (8140)


 
Юрий Зотов ©   (2015-12-16 15:15) [24]

Блин. Начал забывать WinAPI. Но смутно вспомнил, что в описании SendMessage что-то на эту тему говорилось. Решил посмотреть - и вот что увидел:

If the specified window was created by the calling thread, the window procedure is called immediately as a subroutine. If the specified window was created by a different thread, the system switches to that thread and calls the appropriate window procedure. Messages sent between threads are processed only when the receiving thread executes message retrieval code. The sending thread is blocked until the receiving thread processes the message.

То есть, Вы правы - обработчик сообщения выполняется в главном потоке. Но это мало что меняет, потому что сценарий может быть таким:

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

В логе увидим:

Enter поток 1
Enter поток 2
Leave поток 2
Leave поток 1

Значит, если мы хотим видеть "Leave поток N" сразу после "Enter поток N", то нужна критическая секция.


 
sniknik ©   (2015-12-16 15:51) [25]

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

нафиг критическую секцию.... ИМХО. PostMessage рулит! :)


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


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

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


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

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


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

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


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


 
han_malign ©   (2015-12-16 17:42) [28]


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

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

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

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


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

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


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

> 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 - то есть, механизм срабатывает автоматически).


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

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

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


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

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

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


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

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

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


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


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

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

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

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


 
DayGaykin ©   (2015-12-17 10:28) [35]

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


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


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


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


 
Alex4445   (2015-12-17 12:50) [37]


> 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 13:21) [38]

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


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


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

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


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

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


 
sniknik ©   (2015-12-17 14:44) [39]

> либо с Вашим утверждением они не стыкуются
либо ТС вбросил "дезу", у него приведенный код не рабочий, т.е. можно предположить, что "здесь пример,а на самом деле код другой".
[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 и поставил меньше ожидание ускорить процесс "на ошибку".

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


 
sniknik ©   (2015-12-17 14:53) [40]

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


 
sniknik ©   (2015-12-17 15:01) [41]

!!!!
> разве что в 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


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

Рассмотрим 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 17:35) [43]

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

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


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

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


 
sniknik ©   (2015-12-18 14:02) [45]

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



Страницы: 1 2 вся ветка

Форум: "Начинающим";
Текущий архив: 2017.11.12;
Скачать: [xml.tar.bz2];

Наверх





Память: 0.62 MB
Время: 0.003 c
2-1450086211
TheEd
2015-12-14 12:43
2017.11.12
как отловить необходимость погасить кнопку в ячейке StringGrid


2-1450085380
gedevan
2015-12-14 12:29
2017.11.12
Как экспортировать или импортировать таблицу из базы Access


2-1449542054
Арлекино
2015-12-08 05:34
2017.11.12
Корректно ли объявление аргумента как var Param: PByte?


15-1467495002
Юрий
2016-07-03 00:30
2017.11.12
С днем рождения ! 3 июля 2016 воскресенье


15-1464643802
Юрий
2016-05-31 00:30
2017.11.12
С днем рождения ! 31 мая 2016 вторник





Afrikaans Albanian Arabic Armenian Azerbaijani Basque Belarusian Bulgarian Catalan Chinese (Simplified) Chinese (Traditional) Croatian Czech Danish Dutch English Estonian Filipino Finnish French
Galician Georgian German Greek Haitian Creole Hebrew Hindi Hungarian Icelandic Indonesian Irish Italian Japanese Korean Latvian Lithuanian Macedonian Malay Maltese Norwegian
Persian Polish Portuguese Romanian Russian Serbian Slovak Slovenian Spanish Swahili Swedish Thai Turkish Ukrainian Urdu Vietnamese Welsh Yiddish Bengali Bosnian
Cebuano Esperanto Gujarati Hausa Hmong Igbo Javanese Kannada Khmer Lao Latin Maori Marathi Mongolian Nepali Punjabi Somali Tamil Telugu Yoruba
Zulu
Английский Французский Немецкий Итальянский Португальский Русский Испанский