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




Вниз

Вопрос по TThread 


Romul   (2001-12-29 17:10) [0]

У меня есть две процедуры, которые делают некие действия с БД. Количество информации которое они перелопачивают большое, поэтому я эти две процедуры засунул в два потока, только вот как оканчивает работу первая процедура, так она выводит в Label некую цифру, но на самом деле пока не закончил работу второй поток она рисует белое поле. Как можно это исправить? Может можно отложить запуск второго потока, отрисовать Label со значением, а потом запузырить второй поток.



yaJohn   (2001-12-29 17:19) [1]

??? Pdrobnee.



yaJohn   (2001-12-29 17:20) [2]

??? Pdrobnee



panov   (2001-12-29 17:20) [3]

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



Romul   (2001-12-29 17:38) [4]

Вот процедура после выполнения которой выводится количество номеров
procedure Capacity(ADOQuery1:TADOQuery; Label5:TLabel);
var
str:String;
begin
str:="+";
if ADOQuery1.Active then ADOQuery1.Close;
ADOQuery1.SQL.Clear;
ADOQuery1.SQL.Add("Select mb_number from MB where");
ADOQuery1.SQL.Add("mb_active="+QuotedStr(str)+" and mb_number>=convert(bigint, 3000) and mb_number<convert(bigint, 4000)");
try
ADOQuery1.Open;
except
on DBException:Exception do
ShowMessage("Error, mother fucker!");
end;
Label5.Caption:=IntToStr(ADOQuery1.RecordCount);
ADOQuery1.Close;
end;

Эта процедура соответственно запускается после нажатия кнопки следующим образом
T1:=TMyThread.Create(true);
T2:=TMyThread.Create(true);
T1.FreeOnTerminate:=True;
T2.FreeOnTerminate:=True;
T1.Resume;

Потом идет следующее

T2.FreeOnTerminate:=True;
T2.Resume;
А этот поток запускает примерно аналогичную процедурку, но там запрос намного больше записей возвращает, плюс там еще кое-чего надо подсчитать, а потом резалты тоже через лэйблы выводятся



yaJohn   (2001-12-29 17:52) [5]

Pardon, ya ne specialist v ADO, no...
1. Label5.Caption:=IntToStr(ADOQuery1.RecordCount);
Luchshe vinesti v synhronize metod.
2. Gde idet vizov Capacity? V T1.Execute?
3. V lubom sluchae vse budet zaviset" ot togo, kak ADO obrabativaet paralelnie zaprosi. T.e. prihodit zapros1 i do ego
zavershenia prihodit zapros2. Varianti:
a) zapros2 zapustitsia posle zavershenia zaprosa1
b) zaprosi rabotaut paralelno (maloveroyatno)
c) ADO primet oba zaprosa, no rezultati vidast po zavershenii oboih (stranno, no simptomi pohoji).
Variant a) mojno (ya dumau) prinuditelno vizvat vlojiv kajdiy zapros v otdelnuu tranzakciu. V ADO est tranzakcii? %)



panov   (2001-12-29 18:17) [6]

Возьми пример.
У тебя немного не так построена логика:
http://delphi.mastak.ru/cgi-bin/download.pl?get=990523419&n=0



Romul   (2001-12-30 10:14) [7]

2Panov
Спасибо, я уже как-то раньше Ваш пример скачивал, может действительно его использую.
Я тоже думаю, что логика применения потоков в этой проге у меня хромает, но я с потоками первый раз пишу, поэтому малость путаюсь, т.е. не могу понять сразу они в двоем запускаются или нет, и как тогда запуск второго отложить, чтобы лэйбл успел отрисоваться. Или может все запросы к базе сделать в одной процедуре и ее в поток вставить?
2yaJohn
Да, первая процедура вызывается через T1.Execute, а вторая через T2 соответственно.
Все в этой проге работает нормально, кроме того, что лэйблы сначала белым отрисовываются. Сначала после выполнения первой процедуры белым отрисовывается один лэйбл, а после второй процедуры еще три, а через короткий промежуток времени появляются сами результаты в виде цифирей.



panov   (2001-12-30 11:30) [8]

А в начале обработки в потоке этот Label5 у тебя не очищается?



Romul   (2001-12-30 11:52) [9]

Да нет. Как в процедуре первый раз появляется так и все нигде больше не присутствует.
Интересно, а что будет если
.......
Synchronize(procedure1);
Sleep(10000);
Synchronize(procedure2);
...........



panov   (2001-12-30 11:59) [10]

Может быть, тебе привести полностью код твоего TMyThread, а то так гадать будем долго.



Romul   (2001-12-30 12:11) [11]

Кстати при задержке как я и думал Label отрисовывается и там появляется число, а потом второй поток запускается, но прикол в том, что за несколько секунд до появления результатов выполнения второго потока лэйблы (кроме первой) в которые выводятся данные опять отрисовываются белым, ну а потом (5-10 сек) появляются в них числа.
procedure TMyThread.Execute;
begin
{ Place thread code here }
//while not Terminated do
//begin
Synchronize(AbonentsCapacity(ADOQuery1:TADOQuery; Label5:TLabel));
Sleep(10000);
Synchronize(GetPureMessages(ADOQuery1:TADOQuery; ADOQuery2:TADOQuery; Label6:TLabel; Label8:TLabel; Label10:TLabel; DateTimePicker1:TDateTimePicker; DateTimePicker2:TDateTimePicker));
//end;
end;



panov   (2001-12-30 12:23) [12]

У тебя основные вычисления проводятся в контексте основного потока.
Synchronize(AbonentsCapacity(ADOQuery1:TADOQuery; Label5:TLabel));
Замени на
AbonentsCapacity(ADOQuery1:TADOQuery; Label5:TLabel)

(Synchronize выполняется в основном потоке.)



Romul   (2001-12-30 12:38) [13]

Т.е. в Execute убрать синхронизацию первой процедуры, а у второй оставить?



panov   (2001-12-30 13:13) [14]

Те команды и операторы, которые выполня.тся в процедуре, указанной в Synchronize, выполняются в основном потоке.
Свой код, который необходимо выполнять в потоке, весь прописываешь в Execute, а в процедуре, работающей в Synchronize, только производишь обновления, которые выполняются в основном потоке, и, естественно блокируют основной поток.
Т.е.:
procedure MyThread.Execute;
begin
...
// Здесь идет длительная обработка.
...
// После окончания расчетов выполняешь
Synchronize(Capacity);

В Capacity пишешь, например:

MainForm.Label5.Caption := ....
//На этом твой поток завершает работу
end;


Непонятно, зачем тебе ждать завершения первого потока, чтобы выполнить второй.

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

В Capacity пишешь:
begin
Label5.Caption := .... //Необязательно.
PostMessage(Form1.Handle, WM_ENDMYTHREAD1,1);
//или
PostMessage(Form1.Handle, WM_ENDMYTHREAD2,2);
//Только здесь нужно неким образом определить, какой поток завершил работу - первый или второй и соответствующее сообщение посылать
end;

В основной форме описываешь процедуру обработки сообщения:

в const (до определения класса type TForm1=class ...)
const
WM_BASE = WM_USER+1;
WM_ENDMYTHREAD = WM_BASE+1;
в public в определении формы добавляешь
procedure onWM_ENDMYTHREAD(msg: TMessage); Message WM_ENDMYTHREAD;
//посмотри точное определение в процедуры HELP по ключевому слову Message (у меня Delphi нет под рукой)
в секции implementation определяешь эту процедуру
procedure TForm1.onWM_ENDMYTHREAD(msg: TMessage);
begin
if msg.lparam = 1 then TMyThread.Create; //запуск второго потока.
end;

За точный синтаксис не ручаюсь. Вот Delphi сегодня установлю, тогда могу тебе коротенький пример прислать...



Romul   (2001-12-30 13:35) [15]

Да, у меня была задумка использовать PostMessage, но мне показалось (хотя оговорюсь, что я впервые потоки использую, молодой еще :)), что как-то проще можно сделать, т.е. без посылки сообщения форме.
Ладно, лучше обобщу мою задачу. У меня есть три таблицы, из первой мне надо получить кол-во чего-то там, также есть вторая таблица с текстом и датами, когда этот текст был внесен, а третья таблица это просто архив второй (надо выбирать данные за более ранние даты чем во второй таблице). Так вот обработка первой таблицы, т.е. получение некой суммы занимает мало времени, а вторая и третья таблицы долго обрабатываются, так как там до 30 тыс записей приходится выбирать и плюс еще подсчитывать количество символов (!!!) в каждой строке (так надо), так что занимает все это дело где-то минут 6, вот и надо как-то и интерфейс обнавлять и некие действия с данными выполнять.



panov   (2001-12-30 13:48) [16]

Привожу пример работы:
Основная форма (там только одна кнопка - Button1):

unit ThrSample;

interface

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

const
WM_BASE = WM_USER;
WM_ENDTHREAD = WM_BASE+1;
type
TfThrSample = class(TForm)
Button1: TButton;
procedure Button1Click(Sender: TObject);
private
{ Private declarations }
public
procedure onWM_ENDTHREAD(var Message: TMessage); message WM_ENDTHREAD;
end;

var
fThrSample: TfThrSample;

implementation
{$R *.DFM}
procedure TfThrSample.onWM_ENDTHREAD(var Message: TMessage);
begin
if Message.wParam = 1 then TMyThread.Create(fThrSample.Handle,1);
end;

procedure TfThrSample.Button1Click(Sender: TObject);
begin
TMyThread.Create(fThrSample.Handle,1);
end;

end.

И определение потока:

unit uThread;

interface

uses
Classes,windows;

type
TMyThread = class(TThread)
private
FNumThread: Integer;
FHandleForm: Integer;
protected
procedure Execute; override;
public
constructor Create(const aHandleForm, aNumThread: Integer);
procedure DoUpdate;
end;

implementation
uses
ThrSample;

constructor TMyThread.Create(const aHandleForm,aNumThread: Integer);
begin
inherited Create(False);
FreeOnTerminate := True;
FNumThread := aNumThread;
FHandleForm := aHandleForm;
end;

procedure TMyThread.DoUpdate;
begin
PostMessage(FHandleForm,WM_ENDThread,FNumThread,0);
//этот код можно выполниить и в EXECUTE
end;

procedure TMyThread.Execute;
begin
// что-то делаем
Synchronize(DoUpdate);
end;

end.



panov   (2001-12-30 13:59) [17]

Эту строчку замени
if Message.wParam = 1 then TMyThread.Create(fThrSample.Handle,1);
на
if Message.wParam = 1 then TMyThread.Create(fThrSample.Handle,2);

:-)
Иначе плохо будет...



Romul   (2001-12-30 14:20) [18]

Спасибо, а в Execute тогда что получается?
procedure TMyThread.Execute;
begin
MyProcedure1;
Synchronize(DoUpdate);
MyProcedure2;
end;
Или как?



panov   (2001-12-30 14:35) [19]

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

Кстати, в примере такой параметр уже есть - это aNumThread
В Execute пишешь:
case aNumThread of
1:
Действия, какие у тебя в Procedure1 выполняются
2:
Действия, какие у тебя в Procedure2 выполняются
и т.д.



Romul   (2001-12-30 15:21) [20]

Ага, понятно. Спасибо еще раз. Сейчас все это соберу и попробую.




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




Наверх





Память: 0.77 MB
Время: 0.023 c
4-52686           YUS                   2001-11-16 06:36  2002.01.17  
CreateDialog -????


3-52480           grab                  2001-12-14 20:05  2002.01.17  
как закачать в stringgrid данные из Excel


3-52490           Андре                 2001-12-14 11:50  2002.01.17  
Поля массивы в Interbase


4-52685           maxi                  2001-11-12 16:26  2002.01.17  
Как програмно нажать (выбрать) пункт меню из ТMainMenu ?


1-52552           Slawik                2001-12-26 11:12  2002.01.17  
колонки в DB гриде ???