المساعد الشخصي الرقمي

مشاهدة النسخة كاملة : دلفي & threads مفاهيم و تطبيقات .


TF6M
11-03-2010, 03:16 PM
بسم الله الرحمان الرحيم
السلام عليكم و رحمة الله و بركاته


كيف نبرمج تطبيقاتنا بطريقة حديثة ؟ , كيف يمكن لنا أن نسرع من أداء برنامجنا دون وقوع مشاكل أو :frusty: , كيف لنا أن تشغيل عدة عمليات
في آن واحد (ظاهريا) بالتوازي و بشكل متزامن (مدروس) ؟ , ببساطة البرمجة بالإعتماد على آلية Multi_Thread .


بعض التوضيحات "نظريا" :
أنظمة التشغيل تتوافق تماما مع هندسة المعالج و آلياته , الأنظمة الحديثة (Multi_Tache) أساسا تعتمد على تقليص زمن التنفيذ كيف ؟
عن طريق آلية Pipeline, معالج يدعم pipeline ينفذ تعليمات برنامج ما "بالتزامن في طور عمل واحد" وفق روتينا المعالج :
Fetch,decode,memory access,write back لاحظ الجدول التالي الدي يوضح آلية معالجة برنامج بـ Pipeline

http://img191.imageshack.us/img191/4423/1561g.jpg

نفرض مثلا أن لدينا برنامج 20 %تعليمات تحميل و تخزين , 40 %تعليمات منطقية و مقارنة ... , 40 %تعليمات قفز و تفرع ...
لو كان المعالج لا يدعم pipeline سيظطر لتنفيذ تعليمة بعد تعليمة و لن يدخل للتعليمة الجديدة حتى ينهي السابقة ! و إذا كان كل صنف
من التعليمات يحتاج طورين أو أكثر لتنفيذ التعليمة الواحد لك أن تتخيل "مدى بطئ معالجة تعليمات البرنامج المحمل الواحد" لكن مع pipline
أطوار عمل أقل و أداء أسرع يمكن أن ننفذ أكثر من برنامج (Process) بشكل متزامن (Synchronize) بإزاحة توافق (Priority) المخصصة
لكل process على أساس Pid(رقم تعريف البروسس), hProcess(مقبض البروسس), لم أشرح كثيرا فقط صطحيا للدخول في جو الدرس ! .

"الان ماذا لو قمنا بنفس عمل نظام التشغيل داخل تطبيقاتنا ؟ يعني أن ننفذ العديد من العمليات بشكل متزامن في آن واحد (ظاهريا) !."

الشكل العام لإنشاء thread واحد كمثال أول , نعتمد على خصائص البرمجة poo بشكل عادي يعني إنشاء
class مورث من TThread في ديلفي .

{$IFDEF VER150}//delphi 7
Type
MyThread = Class (TTHread)
Private
_i:Byte;
Protected
Procedure DoSomeThing;
Procedure Execute;OverRide;
Public
Constructor Create (Suspended:Boolean);
Destructor Destroy;OverRide;
End;
{$ENDIF}

var
MainFrm: TMainFrm;
MyTh :MyThread;

implementation

{$R *.dfm}

Constructor MyThread.Create;
Begin
Inherited Create(Suspended);//If False = start when we call create constructor !.
_i:= 0;//Initialization ...
End;

Destructor MyThread.Destroy;
Begin
//Set Object That Created Free ...
Inherited;//Call the oroginal Destructor (TThread Class).
End;

Procedure MyThread.Execute;
Begin
repeat
Sleep(100); //500 = 1/2 s
Synchronize(DoSomeThing);
until Terminated;
End;

{$X+}// Set extended syntax on

Procedure MyThread.DoSomeThing;
Begin
Inc(_i);
If _i <= 255 Then
MainFrm.Label3.Caption:= IntToStr(_i) Else
_i:= 0;
End;

procedure TMainFrm.Button1Click(Sender: TObject);
begin
MyTh:=MyThread.Create(False);
end;

procedure TMainFrm.Button2Click(Sender: TObject);
begin
Try
MyTh.Suspend;
Except
On E : Exception Do
MessageBoxA(0,Pchar(E.Message),Nil,0);
End;
end;

procedure TMainFrm.Button3Click(Sender: TObject);
begin
Try
MyTh.Resume;
Except
On E : Exception Do
MessageBoxA(0,Pchar(E.Message),Nil,0);
End
end;

procedure TMainFrm.Button4Click(Sender: TObject);
begin
MyTh.Terminate;
//finalization ...
Label3.Caption:= '0';
MainFrm.Button1.Enabled:= True;
end;

procedure TMainFrm.FormCloseQuery(Sender: TObject; var CanClose: Boolean);
begin
Button4Click(Sender);// THread Must Be Terminated!.
ExitProcess(GetLastError);//Exit .
end;

initialization
MyTh:=MyThread.Create(True);
finalization
MyTh.Free;
end.التوجيه IFDEF هنا أظفته متعمدا لتحديد إصدار دلفي المعتمد في التطبيق يمكن تجاوزها (إظافة فقط) كما أني أظفت التوجيه $X
الدي يتيح لنا دمج الدوال و الإجراءات دون مراعات ترتيب الـ implementation (أيضا كإظافة:tong:) ,
قمنا بإدراج initialization / finalization يكفي أن نعمل Resume لبدأ تنفيذ Execute الخاص بالـ Thread آليا !.



- هناك عشرات التعريفات و المقدمات حول موضوع الـ THread لذا كان علي الدخول من منطلق
أخر , هدا التطبيق الأول مثال بسيط للمناقشة ....

http://img715.imageshack.us/img715/9628/myapp.gif

. في هذا المثال قمنا ببرمجة عداد بسيط لن تنجح في تنفيذه دون إستعمال الـ threads! , هنا لم يتجمد البرنامج (freeze)
& النتيجة دورية 1/10 ثانية & تحكم أكثر & أداء أحسن (مثلا إستغنينا عن الـ Timer).

mohfa
11-03-2010, 05:27 PM
اخي الكريم TF6M بارك الله فيك على هكذا درس . و لكن هناك ملاحظة :

لماذا انشئت ال Thread في ال initialization وحررته في ال finalization
:


initialization
MyTh:=MyThread.Create(True);
finalization
MyTh.Free;
end.

ثم صرحت بانشائه في الحدث OnClick للزر :

procedure TMainFrm.Button1Click(Sender: TObject);
begin
MyTh:=MyThread.Create(False);
end;


اقصد ان اقول ان ال Thread قد تم انشاءه مع ال initialization فقط Suspended .

TF6M
11-03-2010, 05:47 PM
شكرا بارك الله فيك ,,

1- لن أناقش مسئلة التحرير:) بل initialization
هنا لكي نبدأ بإستعمال الـ Thread مباشرة لا حظ الـ Start غير مفعلة!
2- قمت بإعادة التصريح ببساطة لإعادة العملية , في حالة
إيقاف الـ Thread بـ Terminate

initialization / finalization عوض إعادة الإنشاء و التحرير
في بداية و نهاية عمل الـ Thread ؟ .

3- نعم قمت بإنشاء الـ Thraed كـ Suspended فقط لإرجاع
أمر بدء تنفيذ Execute لنا (لو كان False لبدأ العد مباشرة
بعد التنفيذ) .

mohfa
11-03-2010, 06:04 PM
ولكن اخي الكريم TF6M هنا سيحدث Thread Collison في حالة تم الظغط على Button1 لانه سوف يتم انشاء نفس ال Thread يمكن ان يكون باHandle جديد و لكن سوف يتم انشاءى و هذا ماسيحدث خلل في تحريره مرة اخرى لا ال OS لايعرف اي Thread سوف يتم تحريره انطلاقا من ال المقبض او ال HANDLE
حاول مثلا عدم استعمال :


ExitProcess(GetLastError);//Exit
.

انت انشئت ال Thread مع ال initialization ولكن SUSPENDED ثم انشئته مرة اخرى و لكن في انتظار او Resuming .اذا كان يمكن القيام بكل هذه العمليم هكذا مثلا :

procedure TMainFrm.Button1Click(Sender: TObject);
begin
MyTh:=MyThread.Create(False);
//..
//do what ever
//..
//..
MyTh.resume;
end;

kachwahed
11-03-2010, 06:58 PM
وعليكم السلام ورحمة الله وبركاته
TF6M واليوم الخميس حان وقت درس نفيس :)
@Mohfa
أولا رد السلام :D
ثانيا: علينا أن نبتعد عن التفاصيل التي لن يفهمها كل من يدخل قسم "دروس دلفي للمبتدئين" لا تنسوا.

لماذا انشئت ال Thread في ال initialization وحررته في ال finalization
صح... أنا أيضا ظننت في البداية أنه قام بإنشاء thREad مرتين، وعندما حملت المثال اتضح خلاف ذلك :)
فالزر Button1 غير مفعل، حتى يتم Terminate للـ Thread.

كنت أعددت مثال صغير لتوضيح مفهوم Threads للمبتدئين، وبعد إذن أخي TF6M سأضيف توضيحات.
http://i42.tinypic.com/opyeww.jpg
حاول النقر على Go no Thread ثم ShowMessage مباشرة
الآن، حاول النقر على Exec Thread ثم على ShowMessage مباشرة
الفرق واضح :)
مبدئيا... Thread هي Process يقوم النظام بتشغيلها عوضا عن برنامجك، وبذلك تستفيد من مزايا زيادة السرعة واقتصاد الذاكرة.

كل Thread له priority تعني أولوية الـ Thread في النظام (وهذا مهم حالة وجود العديد منها).

تنبيه للمبتدئين: حذار الـ Threads ليست للعب! وينبغي التعامل معها بحذر.
مثال مرفق.

ملاحظة: في نسخة دلفي 2010 تم إضافة الأمر Start.
وشكرا على الدرس الرائع
وشكرا لتدخلات الأستاذ Mohfa
تحياتي.

paix144
11-03-2010, 09:35 PM
السلام عليكم
بارك الله فيك أخي TF6M
جاء الدرس في الذي أدرس فيه :rtfm2:
في المارة القادمة إن أمكن أخي العزيز التعمق أكثر :monster2:
أخي كاش واحد لا تنسى درس المؤشرات، أنتظرك

mohfa
12-03-2010, 10:26 AM
اولا السلام عليكم و رحمة الله و بركاته .

كما قلت لك أعدت إنشاءه فقط في حالة إيقافه يعني لا يمكن أن ننشأه مرتين

ولكن ياخي الكريم TF6M اين شرط التاكد من عدمه قبل الانشاء من جديد .
ثانيا حتى و لو كنت قد تاكدت من التوقف من الاحسن تحرير ال Thread قبل انشائه مرة جديد يعني استطيع ان استعمل مثلا :

if terminated then free

لقول مثلا .

ExitProcess(GetLastError);//Exit

هكذا تجبر النظام ان يوقف التطبيق مهما كانت النتائج حتى باستعمال GetLastError
هنا تقول MS من خلال ال msdn ليس من المستحسن استعمال ال ExitProcess مع ال Threads hاو ال Pip lining لماذا :

يمكن ان يكون هناك Infinit او عملية غير منتهية او مايدعى ال Long Process اذا استعملنا ال ExitProcess فهذا سيجبر التطبيق على التوقف مهما كانت النتائج , ومن بين النتائج الخطيرة هي ال System او Memory instability .
في حالتك يمكن استعمال ال Waitfor و هذا سوف يجبر ال Thread التحقق من اي Operation غير منتهية او من عدم تفريغ ال Quee وهذا سوف يحرر كل ال Ressources قبل تحرير ال Thread

اقول و اعيد انك تقوم بانشاء ال Thread مرتين لماذا تتبع معي ال المشهد :

1* عند بداء التطبيق يتم انشاء ال Thread و لكن Suspended يعني بانتطار اي عملية .
2* عندما اظغط على الزر 2 هذا سوف يقوم بتوقيف ال Thread لزمن معين او Suspended .
3* عندما اظغط على الزر 3 هذا سوف يقوم باعادة تشغيل ال Thread بعدما كان Suspended .
4* عندما اظغط على الزر 4 هذا سوف يقوم بتوقيف ال Thread و انهائه و لكن ليس تحريره يعني ال Handle مازال موجود و ال Ressources مازالت في ال Thread Quee .
*5 عندما اظغط على الزر Start ماذا سيحصل ياترى :
سوف يتم اعادة انشاء نفس ال Thread و الذي اصلا مازال موجود و لم يتم تحريره .

6* OnCloseQuery :
سوف يتم مناداة ال المرحلة 4 و يتم غلق التطبيق . رغم ان ال Thread مازال موجود في الذاكرة اقصد مازال الحيز Reserved .

اذا اما ان نستعمل ال :
FreeOnTreminate في وقت الانشاء مع :
Constructor MyThread.Create;
Begin
Inherited Create(Suspended);//If False = start when we call create constructor !.
_i:= 0;//Initialization ...
End;
او نتاكد من تحريره في وقت الهدم :
مع : If terminated then free .
او نستعمل ال Waitfor ثم ال free .

لان terminate لا تحرر ال Thread بل توقفه عن العمل .

انظر هنا مثلا
https://forums.codegear.com/message.jspa?messageID=142218

اتمنى ان الفكرة وصلت .

بارك الله فيك و في اخي kachwahed و كل الاعضاء .

و السلام عليكم

TF6M
12-03-2010, 11:15 AM
الفكرة كلها تتمحور حول إعادة إنشاء الـ Thread دون التأكد من تحريره :)

1- عند غلق البرنامج سيحرر عندالإنهاء , Finalization
2- أقول و أعيد هدا التطبيق الأول , هناك ما يسمى بـ Blocking & Premature Termination
و هي تدخل في الأخطاء التي يقع فيها المبرمج التي يمكن تفاديها بـ Method waitfor
3- أيضا سأتتطرق لـ عيوب التزامن Disadvantage Synchronizer في التطبيق .

- حل هذه المشكلة فقط بإظافة شرط التحرير عند أمر الإنهاء :

Constructor MyThread.Create;
Begin
Inherited Create(Suspended);//If False = start when we call create constructor !.
FreeOnTerminate:= False;
_i:= 0;//Initialization ...
End;و حدف OnCloseQuery أصلا ,,

بارك الله فيك على الإهتمام & الإفادة , شكرا.

kachwahed
12-03-2010, 12:03 PM
في نسخة Delphi 2010 يمكن كتابة إجراء Synchronize كما يلي:
Procedure MyThread.Execute;
Begin
repeat
Sleep(100); //500 = 1/2 s
Synchronize( Procedure ()
Begin
Inc(_i);
If _i <= 255 Then
MainFrm.Label3.Caption:= IntToStr(_i) Else
_i:= 0;
End);
until Terminated;
End;
بالتوفيق أخي TF6M.

TF6M
12-03-2010, 02:55 PM
بسم الله الرحمان الرحيم


تعريف الـ Thread:
- العديد من البرامج (خاصة الحديثة منها) تعتمد على تقنية الـ MultiThreading , هل من الممكن أن نأكل و نشرب و نجري في آن واحد :whistling:!؟ دون عوائق.

الـ thread عبارة عن برنامج (Sous_Programe) , وحدة تنفيذ مستقلة , تتكرر بشكل غير منتهى (حلقة) الى أن يتم إنهائها (Terminate)
يتم تنفيدها من طرف نظام التشغيل على أساس : رقم التعريف (threadID) , أولوية التنفيذ (thread Priority) , مقبض الثريد (thread Handle) .
كما يمكن تعمل عدة threads في آن واحد في برنامج ما , لتحسين أداء & سرعة تنفيذ البرنامج .

التطبيق الثاني :

- برنامج يقوم بقراءة ملف ما Byte By Byte & يعرض بشكل متزامن القيمة المقروؤة & و نسبة القراءة بالنسبة للملف .
- نفس التطبيق السابق من حيث الفكرة , لكن بشكل عملي أحسن :thumbs: .


http://img65.imageshack.us/img65/2439/51651.gif

بالتوفيق ,,

mohfa
12-03-2010, 07:17 PM
السلام عليكم ورحمة الله وبركاته .

يمكن تفادي Synchronization باستعمال ال PostMessage و SendMessage و هنا سوف يتدخل النظام و يصبح هو المتحكم في عمليات ال In/Out لل Thread و هو احسن شئ يمكن استعماله مع ال Thread و هذا سيكون بتعامل مع LPARAM و WPARAM .
انا متاكد ان اخي TF6M سوف يتطرق لها في بقية الدرس ان شاء الله و هو مشكور على هذا .

TF6M
14-03-2010, 04:54 PM
بسم الله الرحمان الرحيم


- لإنشاء thread توفر دلفي صنف TThread لتحقيق ذلك , دون اللجوء الى WinApi , تعرض عدة خصائص
لتسهيل التعامل مع الـ Thread , تتمثل في :


Create/Destroy: كـ Constructor /Destructor .


Execute: يحتوى على الكود الذي سينفذه الثريد , كما يجب التحقق بصفة دورية الخاصية Terminted , لأنه في حالة إقاف عمل الثريد
بواسطة الخاصية Terminate , يجب علينا إيقاف التنفيذ أو معالجة الكود الذي تنفذه الخاصية Execute .


Synchronize: هذه الخاصية لها باراماتر وحيد , يحتوي على إسم إجراء/دالة معرفة في وحدة البرنامج , تستخدم لتجنب
التداخلات التي يمكن أن تحدث في حال وجود أكثر من thread شغال في آن واحد , أو عدة إجراءات/دوال من VCLs كلها تتعامل
مع متغيرات عامة , قابلة للتغير من أي قسم من أقسام البرنامج .
- في حالة إستخدامنا لأكثر من Synchronize ستكون نسبة تعرضنا لحالة توقف تام للبرنامج كبيرة جدا .
- الجدول التالي يوضح فكرة التداخل في حالة أكثر من ثريد شغال يستخدمان نفس المصادر (متغيرات عامة) :

http://img191.imageshack.us/img191/7157/56555.gif


- يجب أن يكون هناك تزامن في تنفيذ التعليمات بين الثريد X,Y و إلا سيحدث تداخل أثناء معالجة تعليمات كليهما , مثلا التعليمة رقم 3
للثريد X تقوم بفتح الملف F للقراءة فقط , و التعليمة رقم 2 للثريد Y تقوم بالكتابة في F , هناك طرق لتفادي هذه المشكلة كإستخدام الصنف
TCriticalSection الذي يمنع الولوج لقسم معين من الكود (الدي نريد حمايته) و حجبه عن الـ Threads الأخرى ,
أيضا هناك دوال Apiتقوم بنفس الدور (InitializeCriticalSection, LeaveCriticalSection...إلخ) .


WaitFor: لعمل الـ Switch بين threads , مثال لدينا ثريد X و ثريد Y , X ينتظر الـ WaitFor الخاصة بـ Y ليبدأ العمل و هكذا ,
يجب مراعات الـ Synchronize الخاصة بـ X&Y , لانه أي خلل منطقي سيوقف البرنامج حالا ,
البديل بإستعمال دوال Api بما يسمى بـ Wait Functions كـ (WaitForSingleObject, WaitForMultipleObjects ....إلخ) .


Terminate/Suspend/Resum: واضحة من إسم الخاصية , إيقاف الثريد نهائيا , تعطيل الثريد (إيقاف لحظي) , متابعة عمل الثريد أين تم تعطيله .

التطبيق الثالث :

- التحكم في 3 ثريد , A,B&C مع إستخدام TCriticalSection لحماية المتغير Gvar , كما يعد خلاصة كل ما سبق !.

http://img191.imageshack.us/img191/4332/56521.gif

بالتوفيق ,,

kachwahed
14-03-2010, 06:23 PM
يمكن تجميد (Lock) الـ Thread ثم إعادة تشغيله (UnLock) لإنجاز أشغال معينة باستخدام TCriticalSection، أو لمجرد Sleep زمني...
مثال:
uses
SyncObjs;

// one lock for each independent resource
var
myLock : TCriticalSection;

// early during application startup:
myLock := TCriticalSection.Create;

procedure TMyThread.Execute;
begin
//... lock it
myLock.Acquire;

// use the shared resource

// unlock it..
myLock.Release;
end;
Acquire (أو Enter أيضا) بلغة API هي EnterCriticalSection وتستخدم للتجميد
Release هي LeaveCriticalSection لفك الحصار وإطلاق سراح الـ Thread
بارك الله لك أخي TF6M
بالتوفيق.

mohfa
14-03-2010, 06:58 PM
Procedure A or B or C.Execute;
Begin
FreeOnterminate:= False;

نحاول قدر المستطاع الابنعاد عن هذا الاسلوب في النصريح بال :
FreeOnterminate:= False / true و ال Thread في حالة التنفيذ .

والسلام عليكم و رحمة الله و بركاته

kachwahed
15-03-2010, 07:58 PM
مكتبة DLLThreadSynchronize لتسهيل Synchronization مع الـ Threads من خلال DLL
التعليقات بالفرنسي، منقولة للفائدة :).

paix144
15-03-2010, 10:12 PM
السلام عليكم
الظاهر أن الجميع يشارك في الموضوع ما شاء الله
إن أمكن إخواني الكرام وضع تعليقات على كل سطر من الكود، فهاذا سيساعدني و يساعد المبتدئين مثيل لضم مفهوم الدرس بسرعة
و جزاكم الله كل خير و فتح لكم أبواب الخير

TF6M
25-03-2010, 07:18 PM
بسم الله الرحمان الرحيم

السلام عليكم و رحمة الله و بركاته



نقوم بإنشاء برنامج , يشغل ثريد a و b في نفس الوقت ؟ , هنا تتم زيادة نسبة :frusty: , لأنه من الممكن أن يتم خطأ في توقيت الـ سويتش بين a ,b .
يمكن أن نظيف حماية للمتغيرات التي من الممكن أن يتم الولوج إليها من أي قسم من أقسام البرنامج (TCriticalSection) ,
لكن أيضا من الممكن أن ننتظر إنتهاء تنفيذ a , لنتيح المجال لتنفيذ b بعد التأكد نهائيا من إنتهاء a ليكون برنامجنا أكثر أمانا ! .

التطبيق الرابع :

1- يقوم A بإيقاف البرنامج مدة 5 ثواني .
2- يقوم B بإنتظار A حتى إنتهاء تنفيذه , بعدها يبدأ B في العمل .
Not Waiting : تنفيذ .
Waiting : إنتظار , (لم يبدأ في التنفيذ فعليا).

http://img214.imageshack.us/img214/8713/453543.gif

برنامج ينفذ 2 Threads في وقت واحد , بإستعمل دالة WaitForSingleObject, كملاحظة نستعمل
WaitForMultipleObjects في حالة وجود أكثر من 2 Threads ؟.
شرح دالة WaitForSingleObject من هنا (http://msdn.microsoft.com/en-us/library/ms687032%28VS.85%29.aspx).

بالتوفيق ,,

TF6M
26-03-2010, 03:25 AM
بسم الله الرحمان الرحيم
السلام عليكم و رحمة الله و بركاته

هده المشاركة لا تحتاج توضيح , كل ما في المثال عبارة عن إنشاء thread و تنفيذه , بالإعتماد على دوال
الـ Apis , إن لم يكن الكود واضح إبحث عن وظيفة هذه الدوال في MSDN , مثلا ؟؟ :

- CreateThread/OpenThread == Constructor .
- CloseHandle/TerminateThread == Terminate/Free .
- ResumeThread/SuspendThread == Suspend/Resume .
- Wait Functions .
- EnterCriticalSection/LeaveCriticalSection == TCriticalSection .
التطبيق الخامس :

تطبيق بسيط يوضح إستخدام Apis , لإنشاء thread و التحكم به مباشرة ! .

http://img291.imageshack.us/img291/3444/546851.gif

بالتوفيق ,,

karamofweb
10-07-2011, 12:01 PM
شكرا على الموضوع الرائع.
لقد لاحظت وجود مشكل و هو حين الضغط على start مرتين او اكثر تزيد سرعة العداد