Форум: "Основная";
Текущий архив: 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.052 c