Форум: "Начинающим";
Текущий архив: 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;
Страницы: 1 2 вся ветка
Форум: "Начинающим";
Текущий архив: 2017.11.12;
Скачать: [xml.tar.bz2];
Память: 0.6 MB
Время: 0.003 c