ورود

View Full Version : مشکل در کارکردن با thread ها



p_ooya
شنبه 20 تیر 1388, 10:20 صبح
سلام به همه.
من در برنامه ام باید به یه بانک راه دور remote کانکت بشم و چند تا کوئری روش بگیرم. چون اتصال به بانک روی هاست و اجرای پرس و جو ها طول میکشه یه فرم خوشامد گذاشتم که در پیش زمینه اش، اتصال و کوئری ها انجام بشه. برای اینکه فرم خوشامد قفل نشه از یه thread استفاده کردم. مشکل اینجاست که با وجود استفاده از thread باز هم فرم قفل میشه و بعد از اینکه اتصال برقرار شد از حالت قفل خارج میشه. من کدی رو که نوشتم قرار میدم :

کد thread :


unit Unit12;

interface
uses
classes,stdctrls,sysutils,Myclasses, Mycall, windows;

type
InitializeThread = class(TThread)
private
i:integer;
ss:string;
procedure setlabel;
procedure localdbconnect;
procedure hostdbconnect;
procedure settingvalues;
procedure settingqueries;
procedure logingin;

public
//------------
protected
procedure execute; override;

end;

implementation

uses unit1,unit2,unit3,unit4, unit13;
//-------------------------------------------------------------------
procedure InitializeThread.setlabel;
begin
WelcomeForm.LabelStatus.Caption:=ss;
end;
//-------------------------------------------------------------------
procedure InitializeThread.settingvalues;
begin

DataModule1.MyConnection_Host.Database:=LoginForm. sEdit4.Text;
DataModule1.MyConnection_Host.Password:=LoginForm. sEdit6.Text;
DataModule1.MyConnection_Host.Username:=LoginForm. sEdit5.Text;
DataModule1.MyConnection_Host.Server:=LoginForm.sE dit3.Text;

DataModule1.MyConnection_Local.Database:=LoginForm .sEdit8.Text;
DataModule1.MyConnection_Local.Password:=LoginForm .sEdit10.Text;
DataModule1.MyConnection_Local.Username:=LoginForm .sEdit9.Text;
DataModule1.MyConnection_Local.Server:=LoginForm.s Edit7.Text;
end;
//-------------------------------------------------------------------
procedure InitializeThread.localdbconnect;
begin

try
DataModule1.MyConnection_Local.Connect;
except
on e:EMyError do
begin
if e.ErrorCode = 1045 then MessageBox(Handle,'.äÇã ˜ÇÑÈÑí íÇ ˜áãå ÚÈæÑ Ïæã ÇÔÊÈÇå ÇÓÊ','!ÎØÇ',MB_OK+MB_ICONERROR);
if e.ErrorCode = 2003 then MessageBox(Handle,'.ÂÏÑÓ íÇ äÇã ÓÑæÑ Ïæã ÇÔÊÈÇå ÇÓÊ','!ÎØÇ',MB_OK+MB_ICONERROR);
if e.ErrorCode = 1049 then MessageBox(Handle,'.äÇã ÈÇä˜ ÇØáÇÚÇÊí Ïæã ÇÔÊÈÇå ÇÓÊ','!ÎØÇ',MB_OK+MB_ICONERROR);
if e.ErrorCode = 2013 then MessageBox(Handle,'.ÊáÇÔ ÈÑÇí ÇÑÊÈÇØ ÈÇ ÓÑæÑ Ïæã ÈíÔ ÇÒ ÍÏ ãÞÑÑ Èå Øæá ÇäÌÇãíÏ','!ÎØÇ',MB_OK+MB_ICONERROR);
end;
end; //local except

end;
//-------------------------------------------------------------------
procedure InitializeThread.hostdbconnect;
begin

try
DataModule1.MyConnection_Host.Connect;
except
on e:EMyError do
begin
if e.ErrorCode = 1045 then MessageBox(Handle,'.äÇã ˜ÇÑÈÑí íÇ ˜áãå ÚÈæÑ Çæá ÇÔÊÈÇå ÇÓÊ','!ÎØÇ',MB_OK+MB_ICONERROR);
if e.ErrorCode = 2003 then MessageBox(Handle,'.ÂÏÑÓ íÇ äÇã ÓÑæÑ Çæá ÇÔÊÈÇå ÇÓÊ','!ÎØÇ',MB_OK+MB_ICONERROR);
if e.ErrorCode = 1049 then MessageBox(Handle,'.äÇã ÈÇä˜ ÇØáÇÚÇÊí Çæá ÇÔÊÈÇå ÇÓÊ','!ÎØÇ',MB_OK+MB_ICONERROR);
if e.ErrorCode = 2013 then MessageBox(Handle,'.ÊáÇÔ ÈÑÇí ÇÑÊÈÇØ ÈÇ ÓÑæÑ Çæá ÈíÔ ÇÒ ÍÏ ãÞÑÑ Èå Øæá ÇäÌÇãíÏ','!ÎØÇ',MB_OK+MB_ICONERROR);
end;
end; //host except

end;
//-------------------------------------------------------------------
procedure InitializeThread.settingqueries;
begin
DataModule1.MyQuery_Local1.Close;
DataModule1.MyQuery_Local1.SQL.Text:='select count(*) as row_c_l from products';
DataModule1.MyQuery_Local1.Open;

DataModule1.MyQuery_Host1.Close;
DataModule1.MyQuery_Host1.SQL.Text:='select count(*) as row_c_h from products';
DataModule1.MyQuery_Host1.Open;

{ if DataModule1.MyQuery_Local1.FieldValues['row_c_l']=DataModule1.MyQuery_Host1.FieldValues['row_c_h'] then
ShowMessage('equal') else ShowMessage('unequal');
}

end;
//-------------------------------------------------------------------
procedure InitializeThread.logingin;
var
password:String;
begin
if DataModule1.MyConnection_Host.Connected=true then
begin
DataModule1.MyQuery_Host1.Close;
DataModule1.MyQuery_Host1.SQL.Text:=('select * from users where username='''+LoginForm.sEdit1.Text+'''');
DataModule1.MyQuery_Host1.Active:=true;
if DataModule1.MyQuery_Host1.RecordCount>0 then
password:=DataModule1.MyQuery_Host1.FieldValues['password'] else MessageBox(Handle,'.äÇã ˜ÇÑÈÑí íÇ ˜áãå Ú龄 ÇÔÊÈÇå ÇÓÊ','!ÎØÇ',MB_OK+MB_ICONERROR);
if password=LoginForm.sEdit2.Text then
begin
user_rights:=IntToBin(DataModule1.MyQuery_Host1.Fi eldValues['privileges'],24);
user_fullname:=DataModule1.MyQuery_Host1.FieldValu es['full_name'];
MainForm.Show;
end else MessageBox(Handle,'.äÇã ˜ÇÑÈÑí íÇ ˜áãå Ú龄 ÇÔÊÈÇå ÇÓÊ','!ÎØÇ',MB_OK+MB_ICONERROR);
end // outer if


else if DataModule1.MyConnection_Local.Connected=true then
begin
DataModule1.MyQuery_Local1.Close;
DataModule1.MyQuery_Local1.SQL.Text:=('select * from users where username='''+LoginForm.sEdit1.Text+'''');
DataModule1.MyQuery_Local1.Active:=true;
if DataModule1.MyQuery_Local1.RecordCount>0 then
password:=DataModule1.MyQuery_Local1.FieldValues['password'] else MessageBox(Handle,'.äÇã ˜ÇÑÈÑí íÇ ˜áãå Ú龄 ÇÔÊÈÇå ÇÓÊ','!ÎØÇ',MB_OK+MB_ICONERROR);
if password=LoginForm.sEdit2.Text then
begin
user_rights:=IntToBin(DataModule1.MyQuery_Local1.F ieldValues['privileges'],24);
user_fullname:=DataModule1.MyQuery_Local1.FieldVal ues['full_name'];
MainForm.Show;
end else MessageBox(Handle,'.äÇã ˜ÇÑÈÑí íÇ ˜áãå Ú龄 ÇÔÊÈÇå ÇÓÊ','!ÎØÇ',MB_OK+MB_ICONERROR);

end //inner if
else
begin
MessageBox(Handle,'.äÇã ˜ÇÑÈÑí íÇ ˜áãå Ú龄 ÇÔÊÈÇå ÇÓÊ','!ÎØÇ',MB_OK+MB_ICONERROR);
end; //else

end;
//-------------------------------------------------------------------
procedure InitializeThread.execute;
begin
ss:='Setting databases connection values.';
Synchronize(setlabel);
Synchronize(settingvalues);

ss:='Connecting local database...';
Synchronize(setlabel);
Synchronize(localdbconnect);

ss:='Connecting host database...';
Synchronize(setlabel);
Synchronize(hostdbconnect);

ss:='Comparing local and remote databases...';
Synchronize(setlabel);
if ((DataModule1.MyConnection_Local.Connected=true) and (DataModule1.MyConnection_Host.Connected=true)) then
Synchronize(settingqueries);

if ((DataModule1.MyConnection_Local.Connected=true) or (DataModule1.MyConnection_Host.Connected=true)) then
Synchronize(logingin);

Terminate;
Exit;

end;
//-------------------------------------------------------------------

end.کد فراخوانی این thread هم اینه :


procedure TLoginForm.sBitBtn1Click(Sender: TObject);
var
myTH : InitializeThread;
begin
WelcomeForm.Show;

try
myTH:=InitializeThread.Create(true);
myTH.FreeOnTerminate:=true;
myTH.Resume;
except
on e:Exception do ShowMessage(e.Message);
end;

end;درمورد کارایی کد مشکلی ندارم و هم اتصال بدون مشکل انجام میشه و تنها مشکل قفل شدن فرمه. اشتباه من کجاست؟

پیشاپیش از راهنمایی شما متشکرم.

tdkhakpur
شنبه 20 تیر 1388, 11:03 صبح
سلام
فکر کنم کد زیر را حذف کنید حل بشه.


myTH.Resume;

p_ooya
شنبه 20 تیر 1388, 14:13 عصر
سلام
فکر کنم کد زیر را حذف کنید حل بشه.


myTH.Resume;

ممنون از توجهتون. اما (ضمن عرض احترام) فکر می کنم اینطور نیست! دو خط بالاتر از همون کدی که شما میگید، سازنده کلاس با رگومان true فراخوانی شده. این یعنی thread بعد از ساخته شدن اجرا نمیشه تا وقتی تابع resume فراخوانی بشه. منبع :

http://www.barnamenevis.org/forum/showthread.php?t=79463

tdkhakpur
شنبه 20 تیر 1388, 14:52 عصر
سلام
من کاری به منابع ندارم ولی شما وقتی یک ترد را create میکنید اعلام true به ترد میگوید که شروع به اجرا کند.
و resum هم به معنای اجرای مجدد هست.(من به این خاطر نظر دادم که شما گفتید فرم شما که بوسیله ترد اجرا و کنترل میشود قفل میکند.)

p_ooya
شنبه 20 تیر 1388, 15:03 عصر
سلام
من کاری به منابع ندارم ولی شما وقتی یک ترد را create میکنید اعلام true به ترد میگوید که شروع به اجرا کند.
و resum هم به معنای اجرای مجدد هست.(من به این خاطر نظر دادم که شما گفتید فرم شما که بوسیله ترد اجرا و کنترل میشود قفل میکند.)

دقیقاً برعکس! این true مربوط به پارامتر Suspended سازنده هست. اما با این وجود من نظر شما رو امتحان کردم و بر خلاف نظر شما thread اصلاً اجرا نشد.


constructor Create(CreateSuspended: Boolean);

vcldeveloper
شنبه 20 تیر 1388, 19:12 عصر
بخاطر این هست که دارید کدهای مورد نظر را از طریق Synchronize اجرا می کنید. قبلا درباره Synchronize و اینکه به چه شکلی عمل میکنه، صحبت کردم. بطور خلاصه، کدی که شما نوشتید، عملا در همون Thread اصلی اجرا میشه. برای توضیحات تکمیلی عبارت Synchronize را جستجو کنید.

tdkhakpur
یک شنبه 21 تیر 1388, 12:04 عصر
سلام


myTH:=InitializeThread.Create(true);
myTH.FreeOnTerminate:=true;
myTH.Resume;
except
on e:Exception do ShowMessage(e.Message);
end;

شرمنده من تا حالا برنامه ای که کنار دستور create تابع resum استفاده شده باشد را ندیده بودم بالاخره این دودستور کنار هم من را به اشتباه انداخت به هر حال گر توانستید resume را به نحوی حذف کنید.
اگر منظورتان فرم خوش آمد باشد داخل کدهای فوق من جایی ندیدم که شما فرم را ببندید.

p_ooya
یک شنبه 21 تیر 1388, 12:45 عصر
بخاطر این هست که دارید کدهای مورد نظر را از طریق Synchronize اجرا می کنید. قبلا درباره Synchronize و اینکه به چه شکلی عمل میکنه، صحبت کردم. بطور خلاصه، کدی که شما نوشتید، عملا در همون Thread اصلی اجرا میشه. برای توضیحات تکمیلی عبارت Synchronize را جستجو کنید.
سپاس.
من با توجه به آنچه شما گفتید ابتدا execute رو به صورت زیر تغییر دادم :

procedure InitializeThread.execute;
begin

PostMessage(WelcomeForm.Handle,WM_THRD_MSG,0,0);
Synchronize(settingvalues);

PostMessage(WelcomeForm.Handle,WM_THRD_MSG,1,0);
Synchronize(localdbconnect);

PostMessage(WelcomeForm.Handle,WM_THRD_MSG,2,0);
Synchronize(hostdbconnect);


PostMessage(WelcomeForm.Handle,WM_THRD_MSG,3,0);
Synchronize(settingqueries);

Terminate;
Exit;

end;
در واقع ارتباط با فرم رو از طریق پیام انجام دادم. ولی خب باز هم فرق نکرد. بعد کد رو به صورت زیر تغییر دادم :


procedure InitializeThread.execute;
begin
PostMessage(WelcomeForm.Handle,WM_THRD_MSG,0,0);
settingvalues;

PostMessage(WelcomeForm.Handle,WM_THRD_MSG,1,0);
localdbconnect;

PostMessage(WelcomeForm.Handle,WM_THRD_MSG,2,0);
hostdbconnect;

PostMessage(WelcomeForm.Handle,WM_THRD_MSG,3,0);
settingqueries;

Terminate;
Exit;

end;

یعنی فراخوانی توابع داخل خودِ کلاس Thread رو به صورت مستقیم انجام دادم و به نظر میاد که مشکل حل شده (آیا واقعاً حل شده؟).
حالا اگه مجموعه کارهایی که انجام داده ام درسته، پس چرا وقتی یه کلاس از نوع thread ایجاد می کنم خود ادیتور دلفی پیشنهاد میده که برای ارتباطش با فرم، از متد سنکرونایز استفاده کنم. از اون گذشته، وقتی شما گفتید که سایت رو دنبال عبارت سنکرونایز بگردم، (تقریباً) تمام نتایج حاصله حکایت از این داشت که باید از همین دستور سنکرون استفاده بشه. در حقیقت تنها جایی که میشد جواب رو پیدا کرد (البته اگر من پیدا کرده باشم) در این پست (http://www.barnamenevis.org/forum/showpost.php?p=623631&postcount=7) از این مقاله (http://www.barnamenevis.org/forum/showthread.php?t=79463) هست.
حالا بالاخره جوابی که من بهش رسیده ام درسته؟

vcldeveloper
چهارشنبه 24 تیر 1388, 02:19 صبح
من با توجه به آنچه شما گفتید ابتدا execute رو به صورت زیر تغییر دادم
کد اول اشتباه هست. چون هم دارید PostMessage می کنید، هم دارید Synchronize را فراخوانی می کنید. قبلا هم توضیح دادم که وقتی متد Synchronize را فراخوانی می کنید، در واقع Thread شما متوقف میشه، کد تابع مورد نظر شما در Thread اصلی برنامه (نه Threadایی که خودتون ساختید)، اجرا میشه، و بعد از اتمام تابع، ،Thread شما به کار خودش ادامه میده. پس اگر حجم بزرگی از کدهای شما توسط متد Synchronize اجرا بشه، عملا بخش بزرگی از کد شما در داخل همان Thread اصلی اجرا میشه، و شما منفعت خاصی از ایجاد Thread جدید بدست نمیارید.

Synchronize یک راه ساده برای تغییر رابط کاربر گرافیکی برنامه از سایر Thread ها (غیر از Thread اصلی) هست. اگر تغییراتی که در رابط کاربر اعمال می کنید (مثلا نمایش Progress bar) ساده هستند، استفاده از Synchronize مشکل خاصی بوجود نمیاره، ولی اگر کدهای تغییر رابط کاربر پیچیده باشند، استفاده از Synchronize مزیت استفاده از Thread را از بین میبره. در اون صورت باید از روش های دیگه ایی برای بروزرسانی رابط کاربر استفاده کنید، مثل ارسال Message به فرم مورد نظر، یا استفاده از هر یک از Synchronization Objectهای سیستم عامل، مثل Event, Critical Section, Mutex، و غیره.

در کد شما، از آنجایی که کدهایی که قرار دارید همگی مرتبط با بانک اطلاعاتی هستند، و ربط خاصی به رابط کاربر ندارند، نیازی به استفاده از متد Synchronize یا سایر روش های همزمان سازی نیست، و کد دوم پست قبلی شما باید بدون مشکل کار کنه.

در ضمن، در داخل متد Execute نیازی به فراخوانی Terminate نیست؛ هر وقت که اجرای Execute تمام شود، Thread مربوطه Terminate می شود. ثانیا، Exit برای خروج از یک تابع است. در پایان یک تابع، بطور خودکار از تابع خارج می شوید، پس نیازی به Exit در انتهای متد Execute هم نیست.

Saeed_m_Farid
چهارشنبه 24 تیر 1388, 12:55 عصر
سپاس.
در واقع ارتباط با فرم رو از طریق پیام انجام دادم. ولی خب باز هم فرق نکرد.
حالا بالاخره جوابی که من بهش رسیده ام درسته؟


کد اول اشتباه هست. چون هم دارید PostMessage می کنید، هم دارید Synchronize را فراخوانی می کنید.

در ادامه صحبت دوستان :


بنظر من تو کد دوم شما عملاً thread و PostMessage هاتون بی مصرف شدند، من احتمال میدم شما منظور آقای کشاورز از PostMessage رو درست متوجه نشدین؛ احتمالاً پیغامی که پست می کنید هیچ جایی هندل نمیشه (در حقیقت پیغام رو میفرستید هوا!) ، یعنی ویندوز پروسیجر موردنظر در یونیت WelcomeForm یا نوشته نشده یا پیغامتون رو درست پردازش نمی کنه.
برای اینکه منظورم رو بهتر برسونم : شما تمام توابعی رو که تو thread نوشتید رو باید ببرید تو یونیت مربوط به WelcomeForm (یا جایی که WelcomeForm بهش دسترسی داره)، بعد یه Windows Procedure (یا Message Handler) بنویسید که توابع از طریق اون اجرا بشن، یعنی وقتی PostMessage می کنید به WelcomeForm، این توابع تو wndproc (همون Message Handler یا Windows Procedure) شما فراخوانی میشن، یه این صورت :

(*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*)

procedure TWelcomeForm.wndproc(var wndMsg: TMessage);
begin
try
case wndMsg.Msg of

WM_THRD_MSG:begin
case wndMsg.WParam of
0:settingvalues;
1:localdbconnect;
2:hostdbconnect;
3:settingqueries;
else // Addlog('Show ERROR!!!)
end;
end;

//...

else inherited;
end;
except on exp:Exception do
// Addlog('Exception on wndproc['+IntToStr(wndMsg.Msg)+']: '+exp.Message);
end;
end;

(*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*)


thread شما ورودی و خروجی اضافی نداره (یعنی سازنده و مخرب رو override نکردین)، و از طرف دیگه برای انجام عملیات منتظر رویدادی نیستید، پس نیازی نیست CreateSuspended رو ture بدین که مجبور باشید بعداً resume هم بکنیدش (با انتساب مقدار false بذارید thread مستقیم کارش رو انجام بده و تموم بشه)
نکته دیگه اینکه thread رو create کردین ولی از myTH هیچ استفاده ای نکردین؛ اگه کاری با myTH ندارید اصلاً نمیخواد instance رو انتساب بدین! یعنی میشه یه سطر :
InitializeThread.Create(true);برای FreeOnTerminate هم میتونید اول InitializeThread.execute بنویسیدش ...

AbiriAmir
یک شنبه 05 مهر 1388, 18:40 عصر
سلام
میدونم تاپیک مال قبله ولی WM_THRD_MSG چیه؟
خیلی لازم دازم
اگه میشه روی اون کد postmessage ها یکم توضیح بدین

vcldeveloper
یک شنبه 05 مهر 1388, 23:23 عصر
WM_THRD_MSG چیه؟
یک نام برای یک message که برنامه نویس برای خودش ایجاد کرده، و هر زمان که اون Thread بخواد اطلاعاتی به Thread اصلی بفرسته، اون اطلاعات را از طریق این message ارسال میکنه.
در اون کد Thread اصلی بعد از دریافت پیام WM_THRD_MSG از Thread مورد نظر، داده های رسیده به همراه پیام (در اینجا پارامتر wparam) را پردازش میکنه، و متناسب با اینکه Thread چه مقداری را از این طریق ارسال کرده، کد خاصی را اجرا میکنه.


اگه میشه روی اون کد postmessage ها یکم توضیح بدین
درباره PostMessage چندین بار در همین تالار توضیح داده شده.

AbiriAmir
سه شنبه 07 مهر 1388, 16:56 عصر
درباره PostMessage چندین بار در همین تالار توضیح داده شده.

من که تو کل سایت سرچ کردم چیزی راجع بهش نبود
فقط یکی دوتا مثال بود

اگه ممکنه مختصر توضیح بدین

پیشاپیش از لطفتون ممنونم . . .

vcldeveloper
سه شنبه 07 مهر 1388, 17:42 عصر
من که تو کل سایت سرچ کردم چیزی راجع بهش نبود
کجا رو سرچ کردید؟!

http://www.barnamenevis.org/forum/tags.php?tag=PostMessage

AbiriAmir
چهارشنبه 08 مهر 1388, 17:55 عصر
ولی تو هیچ کدومشون توضیح کامل و جامعی راجع به PostMessage نبود
یا تفاوت اون با SendMessage بود و یا...
به هر حال تو هیچ کدوم پارامتراش و نحوه کار کردن باهاش و پر کردن پارامتراش توضیح نداده بود
به هر حال...

------------------

یه نمونه رو جناب Mahmood_n گزاشتن
دانلود کردم
مشکلم تقریبا حل شد
به هر حال ممنون...

-------------------

راستی اگه با PostMessage کار کنیم دیگه مشکلی پیش نمیاد؟؟؟
منظورم اینه که دیگه مثل synchronize نیست که در ترید اصلی برنامه اجرا بشه؟؟؟