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

Вниз

Как сделать чтобы форма реагировала во время работы потоков.   Найти похожие ветки 

 
Kolan ©   (2005-05-27 10:09) [0]

Здравствуйте,
 Разбираюсь с потоками, нашел статью:
http://www.realcoding.net/article/view/158
Сделал пример где два потока, один включает флажок CheckBox"а другой выключает.
Если я всё правильно понял, то потоки должны работать себе и работать а форма нормально реагировать на все события.

Но форма как будто висит. Точнее мне всёже удалось передвинуть её, но переместилась она с большой задержкой.

Вот код потока:

type
 TMyThread1 = class(TThread)
 private
    { Private declarations }
 protected
   procedure DoWork;
   procedure Execute; override;
 end;


procedure TMyThread1.Execute;
begin
while not Terminated do
 Synchronize(DoWork);
end;

procedure TMyThread1.DoWork;
begin
 Form1.CheckBox.Checked := True;
end;

Ну соотвественно второй
Form1.CheckBox.Checked := False;

Код главного юнита.

var
 Form1: TForm1;
  T1: TMyThread1;
  T2: TMyThread2;


На форме одна кнопка:
procedure TForm1.StopButtonClick(Sender: TObject);
begin
 if StopButton.Caption = "Ñòîï" then
 begin
   {Ïðåðûâàåì îáà ïðîöåññà}
   T1.Terminate;
   T2.Terminate;
   StopButton.Caption := "Ñòàðò";
 end
 else
 begin
   {Ñîçäàåì è ñðàçó çàïóñêàåì äâà ïðîöåññà}
   T1 := TMyThread1.Create(False);
   T2 := TMyThread2.Create(False);

   StopButton.Caption := "Ñòîï";
 end;
end;


Что я нетак понял или сделал?


 
-=XP=- ©   (2005-05-27 10:13) [1]

while not Terminated do
begin
Synchronize(DoWork);
Sleep(100); // милисекунд
end;


 
ЮЮ ©   (2005-05-27 10:15) [2]

Synchronize(DoWork);

Т.е. вся работа потоков сводится к выполнению методов DoWork в основном кодовом потоке, вместо того, чтобы делать нечто в своем потоке


 
Kolan ©   (2005-05-27 10:16) [3]

Гы я почти так и пытался, но сделал так:

procedure TMyThread1.DoWork;
begin
 Form1.CheckBox.Checked := True;
 Sleep(100);
end;


 
Bel ©   (2005-05-27 10:17) [4]

Естественно, форма будет тормозить, ведь Syncronize работает в контексте основного потока. Обычно в параллельный поток выносят действия, не связанные с интерфейсом, тогда главная форма тормозить не будет.


 
Kolan ©   (2005-05-27 10:20) [5]


> ЮЮ ©   (27.05.05 10:15) [2]


Не понял. Просто этот пример работы с потоками неудачный?

<quote из источника>
А теперь - замечательный пример для изучения Thread! В нижеследующем примере приложение создает два параллельно работающих процесса. Один Thread пытается установить флажок CheckBox1 в положение "включено" (Checked := True), а другой, передергивая первого - в "выключено".
</quote из источника>


Где взять хороший


 
Kolan ©   (2005-05-27 10:21) [6]


> Bel ©   (27.05.05 10:17) [4]

Простой пример можно?


 
-=XP=- ©   (2005-05-27 10:26) [7]

Простой пример можно?

Чем [1] не подходит?


 
begin...end ©   (2005-05-27 10:37) [8]

> Kolan ©   (27.05.05 10:20) [5]

> Где взять хороший

{Delphi}\Demos\Threads


 
Kolan ©   (2005-05-27 10:38) [9]

Всем подходит, я хочу понять просто как потоки использовать.


 
Kolan ©   (2005-05-27 10:39) [10]


> begin...end ©   (27.05.05 10:37) [8]

Угу, посмотрю. :)


 
-=XP=- ©   (2005-05-27 10:42) [11]

В Вашем примере:

Следующий код выполняется в отдельном потоке:
procedure TMyThread1.Execute;
begin
while not Terminated do
Synchronize(DoWork); // Вызывается метод Synchronize для синхронизации с основным потоком
end;


Весь следующий метод выполняется в основном потоке:
procedure TMyThread1.DoWork;
begin
Form1.CheckBox.Checked := True;
end;


Поэтому, если Вы напишите Sleep() в методе DoWork, то тем самым "усыпите" основной поток. Насколько я понял, Вам не это нужно? Поэтому пишите Sleep() в том месте, где "усыпление" дополнительного потока не вызовет "усыпление" основного потока, то есть, вне методов, вызванных через Synchronize().

Итого:

procedure TMyThread1.Execute;
begin
 while not Terminated do
 begin
   Synchronize(DoWork);
   Sleep(100); // Заснет только этот поток; основной поток продолжит выполнение.
 end;
end;

procedure TMyThread1.DoWork;
begin
 Form1.CheckBox.Checked := True;
end;


 
Polevi ©   (2005-05-27 11:25) [12]

>-=XP=- ©   (27.05.05 10:42) [11]
иногда лучше жевать, ей богу


 
Игорь Шевченко ©   (2005-05-27 11:29) [13]

Архангельский маст дай.


 
-=XP=- ©   (2005-05-27 11:29) [14]

Polevi ©   (27.05.05 11:25) [12]

Можно поподробнее?


 
TUser ©   (2005-05-27 11:33) [15]

> Polevi ©   (27.05.05 11:25) [12]
> Игорь Шевченко ©   (27.05.05 11:29) [13]

А что он сказал не так? Весь Execute не синхронизируется, синхронизируется только метод, который работает с VCL - как и должно быть.
Другой вопрос, что для такой цели отд. поток не нужен, но если все-таки потоком это делать, то я не понял, где в [11] ошибка.


 
Alexander Panov ©   (2005-05-27 11:45) [16]

Посмотри на такой пример.

unit Unit1;

interface

uses
 Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
 Dialogs, StdCtrls, SyncObjs;

type
 TForm1 = class(TForm)
   CheckBox1: TCheckBox;
   Button1: TButton;
   procedure Button1Click(Sender: TObject);
 private
   { Private declarations }
 public
   { Public declarations }
 end;

 TTestThread=class(TThread)
 private
   FCS: TCriticalSection;
   FCB: TCheckBox;
   FInterval: Cardinal;
   FChecked: Boolean;

   procedure Lock;
   procedure Unlock;
   function GetInterval: Cardinal;
   procedure SetInterval(const Value: Cardinal);
   procedure _Sleep;
 protected
   procedure UpdateControl;
   procedure Execute; override;
 public
   constructor Create(Control: TCheckBox; Interval: Cardinal; Checked: Boolean);
   destructor Destroy; override;

   property Interval: Cardinal read GetInterval write SetInterval;
 end;

var
 Form1: TForm1;

implementation

{$R *.dfm}

{ TTestThread }

constructor TTestThread.Create(Control: TCheckBox; Interval: Cardinal;
 Checked: Boolean);
begin
 inherited Create(True);
 Randomize;
 FCS := TCriticalSection.Create;
 FCB := Control;
 FInterval := Interval;
 FChecked := Checked;
 FreeOnTerminate := True;
 Resume;
end;

destructor TTestThread.Destroy;
begin
 FCS.Free;
 inherited;
end;

procedure TTestThread.Execute;
begin
 while not Terminated do
 begin
   Synchronize(UpdateControl);
   _Sleep;
 end;
end;

function TTestThread.GetInterval: Cardinal;
begin
 Lock;
 try
   Result := FInterval;
 finally
   Unlock;
 end;
end;

procedure TTestThread.Lock;
begin
 FCS.Enter;
end;

procedure TTestThread.SetInterval(const Value: Cardinal);
begin
 Lock;
 try
   FInterval := Value;
 finally
   Unlock;
 end;
end;

procedure TTestThread.Unlock;
begin
 FCS.Leave;
end;

procedure TTestThread.UpdateControl;
begin
 FCB.Checked := FChecked;
end;

procedure TTestThread._Sleep;
var
 Times: Cardinal;
begin
 if FInterval=0
   then Times := 10+Random(200)
   else Times := FInterval;
 Sleep(Times);
end;

procedure TForm1.Button1Click(Sender: TObject);
var
 t1,t2: TTestThread;
begin
 t1 := TTestThread.Create(CheckBox1,0,True);
 t2 := TTestThread.Create(CheckBox1,0,False);
end;

end.


 
-=XP=- ©   (2005-05-27 11:52) [17]

Извините, может, я и не прав, но зачем в

procedure TTestThread.SetInterval(const Value: Cardinal);
function TTestThread.GetInterval: Cardinal;

используется критическая секция? Или это для примера?


 
Alexander Panov ©   (2005-05-27 12:00) [18]

-=XP=- ©   (27.05.05 11:52) [17]
используется критическая секция? Или это для примера?


Изменять значение FIinterval могут несколько потоков одновременно.


 
-=XP=- ©   (2005-05-27 12:15) [19]

Изменять значение FIinterval могут несколько потоков одновременно.

Это понятно. Просто FInterval имеет тип Cardinal. Насколько я понимаю, защита читаемой/записываемой памяти реализована на уровне ОС, если не процессора(ов). То-есть, в любом случае будет записано/прочитано верное значение. В смысле, потоки не будут, в случае возникновения конкурентной ситуации, портить (именно портить) данные. То-есть, если в один момент времени два потока решат писать в эту переменную данные, то после того, как они оба завершат сие действие, в переменной останется последнее записанное значение (не важно кем). Мусор там, по идее не появится. Под "мусором" я имею в виду: писали из одного потока 10, из другого 20, а получили в переменной 18.
Или, все же, такая ситуация может произойти?

Я так думаю, критическую секцию надо использовать только тогда, когда необходимо выполнить несколько неразрывно связанных последовательных действий. Например, одновременно установить значения нескольких переменных. В таком случае критическая секция необходима для обеспечения непротиворечивости значений этих переменных. То-есть, когда между изменениями двух (логически) связанных переменных процесоор может переключится на выполнение другого потока. То-есть, некое подобие "транзакции" (прошу прощения за подобную аналогию, но мне кажется, здесь есть сходство).

Хотя, и в [16] она не принесет вреда, разве что некоторое небольшое сокращение производительности.


 
Erik1 ©   (2005-05-27 12:16) [20]

А вобщето господину "Карих Николай" - это автор статьи, надо яйца отрезать. За глубокое непонимание потоков и их синхронизации, а то, что он читает Архангельский, тоглько усигубляет его вину и неможет служить смягчающим обстоятельством.
 Вывод: Неходи больше на этот сайт и не читай глупости.


 
Kolan ©   (2005-05-27 12:18) [21]

Благодарю за ответы.

На самом деле
 Я хочу написать dll. Для работы с Com портом. С записью проблем нет. Вот чтение хочу сделать в потоке. Нашел статью
http://articles.org.ru/docum/comport.php
И решил для начала разабраться с потоками. Мне подсказали что можно использовать механизм сообшений. Однако если я хочу работать в режиме: "послал команду, получил ответ(ответ 2-3байта), обработал", то этот подход непройдет, так как медленно, и надо делать буфер итд.

Как можно решить эту проблемму. Хотелось бы именно работать в режиме послал принял. Скорость порта 115200. И именно dll. чтобы изменить если что.


 
Игорь Шевченко ©   (2005-05-27 12:24) [22]


> Изменять значение FIinterval могут несколько потоков одновременно.


Это как ? Переменная же является приватной для каждого потока ?


 
-=XP=- ©   (2005-05-27 12:28) [23]

Это как ? Переменная же является приватной для каждого потока ?

Про помощи метода SetInterval().

Речь идет о необходимости критической секции внутри методов SetInterval/GetInterval.


 
Kolan ©   (2005-05-27 12:32) [24]


> Erik1 ©   (27.05.05 12:16) [20]

Ну дык ктож знал. Чё первое попалось то и прочел. :).


 
Игорь Шевченко ©   (2005-05-27 12:33) [25]

-=XP=- ©   (27.05.05 12:28) [23]


> Про помощи метода SetInterval().


Угу, сообразил.

В таком случае, разумно и чтение в методе _Sleep защитить критической секцией ?


 
-=XP=- ©   (2005-05-27 12:46) [26]

В таком случае, разумно и чтение в методе _Sleep защитить критической секцией ?

Вот по этому поводу и вопрос: Нужно ли чтение/запись переменной типа Cardinal (32 бита) защищать критической секцией?

То-есть, может ли поток быть приостановлен (для передачи процессора в ведение другого потока) в ходе выполнения чтения/записи этой переменной? Или, если это многопроцессорная система, то может ли ячейка памяти читаться одним процессором в тот же момент, когда другой процессор производит в нее запись? Защищена ли память от таких одновременных операций (то-есть, один процессор пишет старший байт 32-битной переменной, а другой читает младший байт)? Является ли операция (сохранение, например, значения из регистра процессора в ячейку памяти) атомарной по сути своей?


 
Игорь Шевченко ©   (2005-05-27 12:57) [27]


> То-есть, может ли поток быть приостановлен (для передачи
> процессора в ведение другого потока) в ходе выполнения чтения/записи
> этой переменной?


В ходе записи - не может. В ходе чтения, а конкретно в методе _Sleep - может, теоретически. То есть, критическая секция здесь ни к чему.


 
TUser ©   (2005-05-27 12:59) [28]

И все-таки что неправильно в [11]?


 
Digitman ©   (2005-05-27 13:07) [29]


> Является ли операция (сохранение, например, значения из
> регистра процессора в ячейку памяти) атомарной по сути своей?


за ответом на вопрос заглянем в мануал Интела

смотрим, что Интел говорит о префиксе LOCK, который собственно и делает инструкцию атомарной

Causes the processor’s LOCK# signal to be asserted during execution of the accompanying
instruction (turns the instruction into an atomic instruction). In a multiprocessor environment,
the LOCK# signal insures that the processor has exclusive use of any shared memory while the
signal is asserted.
Note that in later IA-32 processors (including the Pentium 4, Intel Xeon, and P6 family proces-sors),
locking may occur without the LOCK# signal being asserted. See IA-32 Architecture
Compatibility below.
The LOCK prefix can be prepended only to the following instructions and only to those forms
of the instructions where the destination operand is a memory operand: ADD, ADC, AND,
BTC, BTR, BTS, CMPXCHG, CMPXCH8B, DEC, INC, NEG, NOT, OR, SBB, SUB, XOR,
XADD, and XCHG. If the LOCK prefix is used with one of these instructions and the source
operand is a memory operand, an undefined opcode exception (#UD) may be generated. An
undefined opcode exception will also be generated if the LOCK prefix is used with any instruc-tion
not in the above list. The XCHG instruction always asserts the LOCK# signal regardless of
the presence or absence of the LOCK prefix.
The LOCK prefix is typically used with the BTS instruction to perform a read-modify-write
operation on a memory location in shared memory environment.
The integrity of the LOCK prefix is not affected by the alignment of the memory field. Memory
locking is observed for arbitrarily misaligned fields.

мы видим, что MOV среди инструкций, к которым применим этот префикс, отсутствует, и это дает повод говорить об атомарности инструкции по дифолту


 
-=XP=- ©   (2005-05-27 13:32) [30]

2 Digitman

 Спасибо


 
Defunct ©   (2005-05-27 13:44) [31]

Digitman ©   (27.05.05 13:07) [29]

> мы видим, что MOV среди инструкций, к которым применим этот префикс, отсутствует, и это дает повод говорить об атомарности инструкции по дифолту

Нет, такого повода, это нам не дает. В списке также отсутствуют строковые команда MOVS/STOS/LODS, которые впринципе не атомарны.


 
Alex Konshin ©   (2005-05-27 13:45) [32]

Alexander Panov ©   (27.05.05 11:45) [16]
Несколько важных замечаний по примеру:

1. Пока не буду вдаваться в обсуждение нужно ли защищать изменение FInterval. Просто хочу указать, что если уж нужно, то довольно часто можно обойтись вызовом одной из из функций Interlocked* (например, InterlockedExchange, InterlockedIncrement) или же коммандой в ассемблере с модификатором lock.

2.Применять критические секции, как и другие средства блокировки нужно обдуманно и обоснованно. В противном случае можешь иметь проблемы с deadlock. Особенно осторожно нужно быть в случае вложенных критических секции (с несколькими критическими секциями). В этом случае deadlock довольно просто получить, если в разных последовательностях исполнения вложение происходит в разном порядке. Вложенные критические секции могут получится, например, в результате прямого или косвенного вызова методов, которые используют другую критическую секцию. Еще обратите внимание, что под "критические секции" в этом пункте я подразумеваю не только TRtlCriticalSection и его производные, но и семафоры и т.п., т.е. все, что может блокировать исполнение.

3. Когда нужно применять критические секции или другие способы синхронизации доступа? Очень распространенная ошибка начинающих (и не только) в том, что они считают, что защиты требуют только глобальные переменные. Другое заблуждение мы только что видели: то, что не нужно защищать приватные переменные. Третье заблуждение мы тоже только что видели: то, что нужно защищать только тогда, когда изменяется более одной переменной. На самом же деле нужно защищать тогда и только тогда, когда:
- Существует возможность доступа к этому ресурсу несколькоми потоками (при этом абсолютно не играет роли, что этот ресурс - приватная переменная какого-то объекта, пусть даже наследника TThread).
- Операция с этим ресурсом неатомарная и изменение ресурса  зависит от его текущего состояния/значения. Например, если вы читаете некую переменную, сравниваете ее с чем-нибудь и в зависимости от результата можете изменить ее значение, то эта операция неатомарная (кстати, ее часто можно заменить на одну из атомарных функций Interlocked*). Если вы даже просто прибавляете к некому полю некое число, то это тоже неатомарная операция (опять-таки, это можно сделать с помощью атомарной InterlockedIncrement).


 
Alex Konshin ©   (2005-05-27 13:52) [33]

О, пока писал, тут уже понаписали.

Digitman ©   (27.05.05 13:07) [29]
Да, mov атомарный, но нужно пояснить, что из этого не следует, что любое присваивание в  Delphi - атомарно, потому значение в регистре должно еще как-то оказаться. Простой пример: два переменные и мы присваиваем значение одной переменной другой. Это - неатомарная операция. Вот присваивание константы - атомарная операция.


 
Суслик ©   (2005-05-27 13:55) [34]

Еще неатомарная операция: присваивание константы значению типа int64


 
Германн ©   (2005-05-27 13:55) [35]

2Игорь Шевченко ©   (27.05.05 11:29) [13]
>Архангельский маст дай.

А это не Архангельский! Это тот же Карих Николай, которого Digitman терпеть не может. Странно, что последний в данном случае промолчал. :)


 
Defunct ©   (2005-05-27 13:56) [36]

Alex Konshin ©   (27.05.05 13:52) [33]

> Да, mov атомарный, но нужно пояснить

А как же быть с Movs? Тоже атомарный?

Интересует что же происходит с сигналом Lock# при отработке строковых операций.


 
Digitman ©   (2005-05-27 14:04) [37]


> В списке также отсутствуют строковые команда MOVS/STOS/LODS,
> которые впринципе не атомарны


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


 
Alex Konshin ©   (2005-05-27 14:04) [38]

Цепочечные операции точно неатомарные, потому что они могут быть прерваны.


 
Digitman ©   (2005-05-27 14:10) [39]


> Alex Konshin ©   (27.05.05 14:04) [38]


ну эт ты загнул)

прерваны они, конечно, м.б., но только не одиночные (т.е. если используется REPХХ-префикс)


 
Alex Konshin ©   (2005-05-27 14:14) [40]

Digitman ©   (27.05.05 14:10) [39]
> Alex Konshin ©   (27.05.05 14:04) [38]
ну эт ты загнул)
прерваны они, конечно, м.б., но только не одиночные (т.е. если используется REPХХ-префикс)

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


 
Digitman ©   (2005-05-27 14:21) [41]


> Alex Konshin ©   (27.05.05 14:14) [40]


> значения в регистрах должны как-то оказаться


ну мы ж - конкретно о LODS/STOS/MOVS/CMPS)... а не об иных инструкциях, которые нужны для начальной инициализации используемых цеп.инструкциями регистров


 
Anatoly Podgoretsky ©   (2005-05-27 14:21) [42]

Digitman ©   (27.05.05 14:10) [39]
А это уже означает агрегатные, они к тому же еще и чрезвычайно медленные на современных процессорах. Хорощо что Борланд не купился на них в своем компиляторе.


 
Alex Konshin ©   (2005-05-27 14:36) [43]

Digitman ©   (27.05.05 14:21) [41]
ну мы ж - конкретно о LODS/STOS/MOVS/CMPS)... а не об иных инструкциях, которые нужны для начальной инициализации используемых цеп.инструкциями регистров

В таком случае почти все инструкции - атомарные. Кроме тех, что с двойным обращением к памяти (к некоторым из них и можно применить lock). Только Delphi-то от этого какой прок?
Кстати, movs с двойным обращением, нужно еще посмотреть, что там про нее написано, скорее всего это и не атомарная инструкция вовсе, неясно, что же будет при паралельном обращении другим процессором к ячейке, в которую мы пишем.


 
Digitman ©   (2005-05-27 14:43) [44]


> Anatoly Podgoretsky ©   (27.05.05 14:21) [42]
> Хорощо что Борланд
> не купился на них в своем компиляторе


в смысле ?


 
GuAV ©   (2005-05-27 22:11) [45]

Anatoly Podgoretsky ©   (27.05.05 14:21) [42]
не купился на них


?

var
 A, B: array[Byte] of Byte;
 C, D: array[0..19] of Byte;

A := B;
lea esi,[esp+$00000100]
mov edi,esp
mov ecx,$00000040
rep movsd

C := D;
lea esi,[esp+$00000214]
lea edi,[esp+$00000200]
movsd
movsd
movsd
movsd
movsd


 
Defunct ©   (2005-05-28 00:02) [46]

GuAV ©   (27.05.05 22:11) [45]

Все иногда ошибаются.

Насчет атомарности mov, и строковых операций movs, lods, stos, scas, cmps:

склоняюсь к тому, что все эти инструкции являются атомарными в чистом виде. Так как не используется метод чтение-модификация-запись.


 
Verg ©   (2005-05-28 07:21) [47]

В многопроцессорных системах разделение шины арбитром происходит между циклами шины.
Сколько циклов шины займет атомарная на первый взгляд операция mov [ebx], eax  если, например, ebx = 0x789FF ?


 
isasa ©   (2005-05-28 10:39) [48]

procedure TMyThread1.DoWork;
begin
Form1.CheckBox.Checked := True;
end;
Ну соотвественно второй
Form1.CheckBox.Checked := False;

В многопоточном варианте эта конструкция не есть гуд.
Надо использовать  SendMessage.


 
-=XP=- ©   (2005-05-28 10:48) [49]

В многопоточном варианте эта конструкция не есть гуд.
Надо использовать  SendMessage.


Можете пояснить?


 
isasa ©   (2005-05-28 11:17) [50]

Опять же. Вот фрагмент из Делф. хелп файла
($DELPHI)\Common Files\Borland Shared\MSHelp\WIN32.HLP

The SendMessage function sends the specified message to a window or windows. The function calls the window procedure for the specified window and does not return until the window procedure has processed the message. The PostMessage function, in contrast, posts a message to a thread"s message queue and returns immediately.

LRESULT SendMessage(

   HWND hWnd, // handle of destination window
   UINT Msg, // message to send
   WPARAM wParam, // first message parameter
   LPARAM lParam  // second message parameter
  );


Parameters

hWnd

Identifies the window whose window procedure will receive the message. If this parameter is HWND_BROADCAST, the message is sent to all top-level windows in the system, including disabled or invisible unowned windows, overlapped windows, and pop-up windows; but the message is not sent to child windows.

Msg

Specifies the message to be sent.

wParam

Specifies additional message-specific information.

lParam

Specifies additional message-specific information.



Return Values

The return value specifies the result of the message processing and depends on the message sent.

Где "handle of destination window" Form.Handle

Классики не рекомендуют использовать прямую адресацию между потоками. Жотя здесь маршаллинг не нужен.
Кстати, меня гложет сомнения, что все это работает отдельными потоками.

Я пользуюсь функцией CreateThread (и не заморачиваюсь на TThread)

 TmainForm = class(TForm)
...
 private
   aThreads : aTHandle;
   aThreadIDs : aDWORD;
   activeThreads : integer;
   procedure WMCopyData(var wMsg : TWMCopyData); message WM_COPYDATA;
 public
   cURL : TCheckURL;
 end;
...
procedure TmainForm.WMCopyData(var wMsg: TWMCopyData);
var wmData : PCopyDataStruct;
   mpURL : pURLs;
begin
 wmData:=wMsg.CopyDataStruct;
 mpURL:=wmData.lpData;
...
....
 cURL.msgWnd:=self.Handle;
 hThread:=CreateThread(nil, 0, @CheckURL, @cURL, 0, ThreadID);
 if hThread=0 then Header.Lines.Add("No Thread!");

где поток описан как

 TCheckURL = record
   msgWnd : HWND;
...
 end;
 pURLs = ^TCheckURL;

function CheckURL(p : pointer) : longint;   stdcall;
var cURL : pURLs;
begin
 Result:=0;
 cURL:=pURLs(p);

...
 SendMessage(cURL^.msgWnd, WM_COPYDATA, 0, longint(@wmData));
....
end;

stdcall обязательно.


 
-=XP=- ©   (2005-05-28 11:24) [51]

Классики не рекомендуют использовать прямую адресацию между потоками.

Потоки одного процесса выполняются в едином адресном пространстве. Поэтому с этим проблем нет.

SendMessage отправляет сообщение не в очередь, а напрямую в оконную процедуру. Поэтому, без синхронизации потоков, эта функция использоваться не может. Для подобного решения можно посоветовать использовать PostMessage.

Но если используется Synchronize(), то приведенный автором вопроса код - совершенно корректный. Зачем в данном случае использовать WinAPI, если можно воспользоваться VCL?


 
isasa ©   (2005-05-28 11:30) [52]

Немного прокоментирую.
Собственно вызов потока
 cURL.msgWnd:=self.Handle; // Это хандлер формы куда передается сообщение
 hThread:=CreateThread(nil, 0, @CheckURL, @cURL, 0, ThreadID);

это структура, которая передается потоку
 TCheckURL = record
   msgWnd : HWND;
   mainForm: TForm;
...
 end;
 pURLs = ^TCheckURL;

в нее, например, можно пихнуть все, что угодно, даже TForm.
Но, опять же, напомню прямую адресацию не использовать - дурной тон.

Хотя из потока

mainForm.myControl.Text:="Оба-на!";

работать будет.


 
isasa ©   (2005-05-28 11:40) [53]

По поводу VCL и потоков см. Ч.Калверта

The WM_COPYDATA message is sent when an application passes data to another application.

Remarks

An application must use the SendMessage function to send this message, not the PostMessage function.


поэтому SendMessage


 
-=XP=- ©   (2005-05-28 11:41) [54]

Но, опять же, напомню прямую адресацию не использовать - дурной тон.

Я так понимаю, в контексте сказанного Вами, это опечатка?
Объясните, в чем же проявляется "дурной тон" в данном случае?
Какие последствия могут возникнуть в результате прямого обращения к объектам VCL в данном примере (с использованием Synchronize)? Обращение идет в контексте основного потока. Адресное пространство - одно и то же. RTTI присутствует и непротиворечива.
Что Вы имеете в виду, говоря "дурной тон"?

P.S. Напомню, что мы сечас все же говорим о TThread, а не о работе с потоками напрямую через WinAPI.


 
isasa ©   (2005-05-28 11:53) [55]

Не поленился, нашел.
Ссылка правда старовата , но
Ч.Калверт. Энциклопедия пользователя. стр.134.
пример один к одному,
коментарии
".. главный поток приложения вообще не сможет выполняться, пока не завершится вызов функции Synchronize


 
-=XP=- ©   (2005-05-28 12:06) [56]

главный поток приложения вообще не сможет выполняться, пока не завершится вызов функции Synchronize

Неверная формулировка.
Главный поток продолжает выполняться. В нем выполняется метод, переданный в качестве аргумента методу Synchronize. Работа метода Synchronize заключается в том, что он передает в главный поток сообщение с указателем на метод, переданный в качестве аргумента, и ожидает окончания обработки этого сообщения, то-есть, вторичный поток останавливается. По факту получения сообщения, главный поток выполняет код переданного в качестве параметра метода в совем собственном контексте. По окончании выполнения этого метода, главный поток продолжает выполняться дальше, и вторичный поток возвращается к своему выполнению.

P.S. Хотя, говорить о том, какой поток останавливается - не совсем корректно. На самом деле, два потока синхронизируются, и выполняется один программный код на двоих. Хотя, все же, я склонен утверждать, что останавливается вторичный поток, отдавая метод на выполнение в контексте главного потока.


 
isasa ©   (2005-05-28 12:22) [57]

:))))))))
Все претензии к переводчику (П.А.Матлаш гл.1-8)

А если серьезно, в данном случае, проще через API.
По поводу синхронизации.
В данном примере - реакции одного потока на действия другого (исключая главный поток), я не вижу.


 
-=XP=- ©   (2005-05-28 12:38) [58]

Вот "цитаты" из VCL:

procedure TThread.Synchronize(Method: TThreadMethod);
begin
 ...
 FMethod := Method;
 SyncProc.Thread := Self;
 SyncList.Add(@SyncProc); // Добавляем наш метод, который мы хотим выполнить в контексте основного потока, в список ("В очередь, сукины дети, в очередь!" (С))
 ...
 WaitForSingleObject(SyncProc.Signal, INFINITE); // И ждем окончания выполнения метода в контексте основного потока ("Ждут, не дождутся, когда ж наши мамы вернуться " (С))
 ...
end;

procedure TApplication.WndProc(var Message: TMessage);
begin
...
 case Message.Msg of
   ...
   WM_NULL: CheckSynchronize;  //А вот тут основной поток приступает к обработке запросов от вторичных потоков
...
end;

function CheckSynchronize: Boolean;
begin
...
 while SyncList.Count > 0 do // Проходим список потоков, ожидающих выполнения своих методов в контексте основного потока
 begin
   SyncProc := SyncList[0]; // Берем метод для первого ожидающего потока
   SyncList.Delete(0);
   ...
   SyncProc.Thread.FMethod; // А тут и выполнение нашего метода в контексте основного потока
   ...
   SetEvent(SyncProc.signal); // А теперь говорим ожидающему вторичному потоку, что он может продолжить выполнение
 end;
...
end;


Я так думаю, к правильности кода из папки Source\VCL претензий нет? :)

А если серьезно, в данном случае, проще через API.
Ну, это кому как. :)

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


 
-=XP=- ©   (2005-05-28 12:41) [59]

P.S. Прошу прощения, в комментариях "Добавляем наш метод", "Берем метод для первого ожидающего потока" допустил ошибки в описании. На самом деле их надо читать как "Добавляем наш поток" и "Берем первый поток из списка ожидающих потоков".


 
VMcL ©   (2005-05-28 12:48) [60]

>>isasa ©   (28.05.05 11:17) [50]

>Я пользуюсь функцией CreateThread (и не заморачиваюсь на TThread)

Пользоваться функцией CreateThread некошерно. Следует пользоваться функцией BeginThread.


 
VMcL ©   (2005-05-28 12:51) [61]

>>-=XP=- ©   (28.05.05 12:38) [58]

Кстати, так Borland сделал начиная с D6. В D5 было несколько по-другому.


 
Defunct ©   (2005-05-28 19:10) [62]

Verg ©   (28.05.05 07:21) [47]

Один:

Addr Launch (from ebx)
Data (from eax)
W



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

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

Наверх




Память: 0.66 MB
Время: 0.038 c
6-1111742525
X-Disa
2005-03-25 12:22
2005.06.14
Проверка существования файла


3-1115811707
_e_u_
2005-05-11 15:41
2005.06.14
Обновление АДОтаблиц


14-1117005456
Igorek
2005-05-25 11:17
2005.06.14
Как установить на клавиатуру индикатор раскладки?


14-1116877897
Yegorchic
2005-05-23 23:51
2005.06.14
Описание программных средств, для написания сайтов


1-1117051211
Erick
2005-05-26 00:00
2005.06.14
Помогите составить кодик





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
Английский Французский Немецкий Итальянский Португальский Русский Испанский