![]() |
| ||
|
|
Новости |
Поиск |
FAQ |
Статьи |
Книги |
Новости VCL
| | Форумы ЧАТ |
Орешник |
|
| |||
| |||
« предыдущая ветвь | форум | следующая ветвь »
Как сделать чтобы форма реагировала во время работы потоков. [D7, WinXP]
Kolan © (2005-05-27 10:09:00)Здравствуйте,
Разбираюсь с потоками, нашел статью:
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;
Что я нетак понял или сделал?
Defunct (2005-05-28 19:10:00) [1]Verg © (28.05.05 07:21) [47]
Один:
Addr Launch (from ebx)
Data (from eax)
W
VMcL (2005-05-28 12:51:00) [2]>> -=XP=- © (28.05.05 12:38) [58]
Кстати, так Borland сделал начиная с D6. В D5 было несколько по-другому.
VMcL (2005-05-28 12:48:00) [3]>> isasa © (28.05.05 11:17) [50]
> Я пользуюсь функцией CreateThread (и не заморачиваюсь на TThread)
Пользоваться функцией CreateThread некошерно. Следует пользоваться функцией BeginThread.
-=XP=- (2005-05-28 12:41:00) [4]P.S. Прошу прощения, в комментариях "Добавляем наш метод", "Берем метод для первого ожидающего потока" допустил ошибки в описании. На самом деле их надо читать как "Добавляем наш поток" и "Берем первый поток из списка ожидающих потоков".
-=XP=- (2005-05-28 12:38:00) [5]Вот "цитаты" из 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.
Ну, это кому как. :)
В данном примере - реакции одного потока на действия другого (исключая главный поток), я не вижу
Они синхронизируются через главный поток (см. код, приведенный выше) - то есть, оба становятся в очередь и ждут. В этом и заключается синхронизация.
isasa (2005-05-28 12:22:00) [6]:))))))))
Все претензии к переводчику (П.А.Матлаш гл.1-8)
А если серьезно, в данном случае, проще через API.
По поводу синхронизации.
В данном примере - реакции одного потока на действия другого (исключая главный поток), я не вижу.
-=XP=- (2005-05-28 12:06:00) [7]главный поток приложения вообще не сможет выполняться, пока не завершится вызов функции Synchronize
Неверная формулировка.
Главный поток продолжает выполняться. В нем выполняется метод, переданный в качестве аргумента методу Synchronize. Работа метода Synchronize заключается в том, что он передает в главный поток сообщение с указателем на метод, переданный в качестве аргумента, и ожидает окончания обработки этого сообщения, то-есть, вторичный поток останавливается. По факту получения сообщения, главный поток выполняет код переданного в качестве параметра метода в совем собственном контексте. По окончании выполнения этого метода, главный поток продолжает выполняться дальше, и вторичный поток возвращается к своему выполнению.
P.S. Хотя, говорить о том, какой поток останавливается - не совсем корректно. На самом деле, два потока синхронизируются, и выполняется один программный код на двоих. Хотя, все же, я склонен утверждать, что останавливается вторичный поток, отдавая метод на выполнение в контексте главного потока.
isasa (2005-05-28 11:53:00) [8]Не поленился, нашел.
Ссылка правда старовата , но
Ч.Калверт. Энциклопедия пользователя. стр.134.
пример один к одному,
коментарии
".. главный поток приложения вообще не сможет выполняться, пока не завершится вызов функции Synchronize
-=XP=- (2005-05-28 11:41:00) [9]Но, опять же, напомню прямую адресацию неиспользовать - дурной тон.
Я так понимаю, в контексте сказанного Вами, это опечатка?
Объясните, в чем же проявляется "дурной тон" в данном случае?
Какие последствия могут возникнуть в результате прямого обращения к объектам VCL в данном примере (с использованием Synchronize)? Обращение идет в контексте основного потока. Адресное пространство - одно и то же. RTTI присутствует и непротиворечива.
Что Вы имеете в виду, говоря "дурной тон"?
P.S. Напомню, что мы сечас все же говорим о TThread, а не о работе с потоками напрямую через WinAPI.
isasa (2005-05-28 11:40:00) [10]По поводу 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
isasa (2005-05-28 11:30:00) [11]Немного прокоментирую.
Собственно вызов потока
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:='Оба-на!';
работать будет.
-=XP=- (2005-05-28 11:24:00) [12]Классики не рекомендуют использовать прямую адресацию между потоками.
Потоки одного процесса выполняются в едином адресном пространстве. Поэтому с этим проблем нет.
SendMessage отправляет сообщение не в очередь, а напрямую в оконную процедуру. Поэтому, без синхронизации потоков, эта функция использоваться не может. Для подобного решения можно посоветовать использовать PostMessage.
Но если используется Synchronize(), то приведенный автором вопроса код - совершенно корректный. Зачем в данном случае использовать WinAPI, если можно воспользоваться VCL?
isasa (2005-05-28 11:17:00) [13]Опять же. Вот фрагмент из Делф. хелп файла
($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 10:48:00) [14]В многопоточном варианте эта конструкция не есть гуд.
Надо использовать SendMessage.
Можете пояснить?
isasa (2005-05-28 10:39:00) [15]procedure TMyThread1.DoWork;
begin
Form1.CheckBox.Checked := True;
end;
Ну соотвественно второй
Form1.CheckBox.Checked := False;
В многопоточном варианте эта конструкция не есть гуд.
Надо использовать SendMessage.
Verg (2005-05-28 07:21:00) [16]В многопроцессорных системах разделение шины арбитром происходит между циклами шины.
Сколько циклов шины займет атомарная на первый взгляд операция mov [ebx], eax если, например, ebx = 0x789FF ?
Defunct (2005-05-28 00:02:00) [17]GuAV © (27.05.05 22:11) [45]
Все иногда ошибаются.
Насчет атомарности mov, и строковых операций movs, lods, stos, scas, cmps:
склоняюсь к тому, что все эти инструкции являются атомарными в чистом виде. Так как не используется метод чтение-модификация-запись.
GuAV (2005-05-27 22:11:00) [18]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
Digitman (2005-05-27 14:43:00) [19]
> Anatoly Podgoretsky © (27.05.05 14:21) [42]
> Хорощо что Борланд
> не купился на них в своем компиляторе
в смысле ?
Alex Konshin (2005-05-27 14:36:00) [20]Digitman © (27.05.05 14:21) [41]
ну мы ж - конкретно о LODS/STOS/MOVS/CMPS)... а не об иных инструкциях, которые нужны для начальной инициализации используемых цеп.инструкциями регистров
В таком случае почти все инструкции - атомарные. Кроме тех, что с двойным обращением к памяти (к некоторым из них и можно применить lock). Только Delphi-то от этого какой прок?
Кстати, movs с двойным обращением, нужно еще посмотреть, что там про нее написано, скорее всего это и не атомарная инструкция вовсе, неясно, что же будет при паралельном обращении другим процессором к ячейке, в которую мы пишем.