الفريق العربي للبرمجةأرشيف المنتديات · 2000 – 2023
نسخة أرشيفية للقراءة فقط — التسجيل والمشاركة مغلقان، والمحتوى محفوظ كما كان.

مفهوم تعدد المسالك في نظام التشغيل Windows - الدرس الثاني:

بدأه HaTy في 30 مايو 2009 · 6 رد · 1,752 مشاهدة · في المواضيع والدروس
مشاركة: واتساب X فيسبوك تيليجرام
#1 صاحب الموضوع

post-89451-1198550773.gif

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

الدرس الثاني : العمليات على المسلك Thread Functions

ذكرنا في الدرس السابق ان الدالة CreateThread تعود بمقبض يمكن استخدامه لاحقا وقد اخذنا مثال على استخدام المقبض في الدالة WaitForSingleObject, وقلنا ان الـ Thread يمكن تشغيله مباشرة بعد إنشاءه وذلك باعطاء الوسيط الخامس للدالة CreateThread وهو وسيط تحديد Creation Flag اي " سمة إلانشاء " القيمة صفر 0 , ماذا لو اردنا ان يتم إنشاء المسلك Thread ويوقف " يعلق " عمله لحين استدعائه لاحقأ ؟ كل هذا سنتعرف عليه في هذا الدرس إن شاء الله .

وسنواصل ايظا في هذا الدرس استعراض باقي استخدامات المقبض الذي ترجع به الدالة CreateThread ولكن قبل ذلك لنلقى نظرة اشمل على دالة إنشاء الـ Thread.

:: تحويل نوع Casting دالة الثريد :-

كما تعلمنا سابقا ان الدالة CreateThread تقوم بانشاء مسلك جديد وترجع بمقبض لهذا المسلك الذي تم إنشائه وتعلمنا ان الدالة التي يستدعيها المسلك الجديد تكون من نوع البيانات DWORD WINAPI ولكن ماذا لو كانت الدالة التي سيبدأ منها المسلك من نوع اخر مثلا كأن تكون من النوع void او int فكيف سيتم استخدامها عندئذ؟

الحل بسيط ويكمن في تحويل نوع الدالة Casting باستخدام معامل التحويل (LPTHREAD_START_ROUTINE) امام اسم الدالة عند تمريره كوسيط لدالة الإنشاء .

لتوضيح ذلك لنفرض ان الـ Thread سيبدأ من الدالة الاتية :

void voidFunction(void)
 {
 cout << "This is a void Function" << endl;
 }

ويتم تمريرها الدالة السابقة voidFunctionالدالة إنشاء الثريد CreateThread كالاتي :

DWORD threadID;
 HANDLE ThreadhHandle;
 ThreadhHandle = CreateThread(NULL,0,(LPTHREAD_START_ROUTINE)voidFunction,0,0,&threadID);

:: انشاء الثريد في وضع التعليق Suspention :-

امر اخر اشرنا له سابقا وهو امكانية إنشاء المسلك وتعليق تنفيذه , ويتم بتمرير القيمة CREATE_SUSPENDED لسمة الإنشاء إي Creation Flagكالاتي :

DWORD threadID;
 HANDLE ThreadhHandle;

 ThreadhHandle = CreateThread(NULL,0,(LPTHREAD_START_ROUTINE)voidFunction,0,CREATE_SUSPENDED ,&threadID);

~~~ الدوال المرتبطة بالمسلك Thread Functions ~~~

:: تحديد اولوية Priority عمل الثريد :-

يتم تحديد اولوية الـ Thread باستخدام الدالة SupendThread وهذه الدالة تأخذ وسيط واحد وهو مقبض الثريد المراد تحديد اولويته والشكل العام لها كالاتي :

BOOL SetThreadPriority(
   HANDLE hThread, // handle to the thread
   int nPriority   // thread priority level
 );

هذا الدالة تأخذ وسيطين , الوسيط الاول مقبض الثريد المرادتحديد اولويته , والوسيط الثاني قيمة int تمثل مستوى الاولوية و القيم الاتية تمثل مستويات الاولوية التي يمكن تمريرها للدالة.

THREAD_PRIORITY_LOWEST اولوية اقل بنقطتين من اولوية العملية Process

THREAD_PRIORITY_BELOW_NORMAL اولوية اقل بنقطة من اولوية العملية Process

THREAD_PRIORITY_NORMAL اولوية عادية

THREAD_PRIORITY_ABOVE_NORMAL اولوية اعلى بنقطة من اولوية العملية Process

THREAD_PRIORITY_HIGHEST اولوية اعلى بنقطتين من اولوية العملية Process

THREAD_PRIORITY_TIME_CRITICAL اولوية بمستوى 15

THREAD_PRIORITY_IDLE اولوية بمستوى 1 " شبه خاملة "

وفي حالة نجاح الدالة فإنها ترجع بقيمة لا تساوي صفر nonzero , وفي حالة فشلها فإنها ترجع بالقيمة 0 .

مثال على استخدامها مع مقبض الثريد الذي انشئناه في المثال السابق :

SetThreadPriority( ThreadhHandle , THREAD_PRIORITY_NORMAL );

:: الحصول على اولوية عمل الثريد :-

يمكن الحصول على مستوى اولوية الـ Thread في إي وقت باستخدام الدالة GetThreadPriority وهذه الدالة تأخذ وسيط واحد وهو مقبض الثريد المراد الحصول على مستوى اولويته والشكل العام لها كالاتي :

int GetThreadPriority(
   HANDLE hThread   // handle to thread
 );

في حالة نجاح الدالة فإنها ترجع بقيمة تمثل مستوى اولية الـ Thread, وفي حالة فشلها فإنها ترجع بالقيمة THREAD_PRIORITY_ERROR_RETURN .

مثال على استخدامها مع مقبض الثريد الذي انشئناه في المثال السابق :

GetThreadPriority( ThreadhHandle );

:: الحصول على معرف الثريد :-

يمكن الحصول على معرف الـ Thread في إي وقت باستخدام الدالة GetCurrentThreadId وهذه الدالة ليس لها وسائط وسيط والشكل العام لها كالاتي :

DWORD GetCurrentThreadId(VOID);

وعند تنفيذ الدالة ترجع بقيمة من نوع البيانات DWORD تمثل معرف الـ Thread.

مثال على استخدامها مع مقبض الثريد الذي انشئناه في المثال السابق :

DWORD threadID;
 threadID = GetCurrentThreadId( ThreadhHandle );

:: ايقاف او تعليق Suspension عمل الثريد :-

يمكن ايقاف عمل الـ Thread في إي وقت باستخدام الدالة SupendThread وهذه الدالة تأخذ وسيط واحد وهو مقبض الثريد المراد تعليق تنفيذه والشكل العام لها كالاتي :

DWORD SuspendThread(
   HANDLE hThread   // handle to the thread
 );

مثال على استخدامها مع مقبض الثريد الذي انشئناه في المثال السابق :

SuspendThread( ThreadhHandle );

:: أستئناف عمل الثريد :-

لإستئناف عمل الثريد يتم استخدام الدالة ResumeThread وهي دالة تأخذ وسيط واحد هو مقبض الثريد Threadوالشكل العام لها كالاتي :

DWORD ResumeThread(
   HANDLE hThread   // identifies thread to restart
 );

هذه الدالة ترجع بقيمة suspend count او عدد تكرار ايقاف الثريد عموما ان كانت القيمة تساوي صفر 0 فالثريد لم يتم ايقافه واذا كانت تساوي 1 فالثريد كانت متوقف وتم استئنافه واذا كانت القيمة اكبر من 1 فالثريد لازال متوقف .

في حالة فشل الدالة فإنها ترجع بالقيمة 0xFFFFFFFF .

مثال على استخدامها مع مقبض الثريد الذي انشئناه في المثال السابق :

ResumeThread( ThreadhHandle );

:: انهاء عمل الثريد :-

لإنهاء اي ثريد Thread يجب توفر كود الانهاء ExitCode لذلك الثريد وهذا الكود من نوع DWORD ويتم الحصول عليه باستخدام الدالة GetExitCodeThread والشكل العام لها كالاتي :

BOOL GetExitCodeThread(
   HANDLE hThread,	  // handle to the thread
   LPDWORD lpExitCode   // address to receive termination status
 );

هذا الدالة تأخذ وسيطين , الوسيط الاول مقبض الثريد المراد إنهائه والوسيط الثاني مؤشر لمتغيير يستقبل كود الإنهاء ExitCode .

وفي حالة نجاح الدالة ترجع بقيمة لا تساوي صفر nonzero , وفي حالة فشلها فإنها ترجع بالقيمة 0 .

بعد الحصول على كود الإنهاء ExitCode يتم إنهاء الثريد باستخدام احدى دالتين يوفرهما نظام التشغيل وهما الدالة ExitThread , وهي الافضل والآمن والدالة الاخرى هي TerminateThread وتعتبر اكثر خطورة و ينصح باستعمالها في اضيق الحدود .

:: اولا - الدالة ExitThread و الشكل العام لها كالاتي :

VOID ExitThread(
   DWORD dwExitCode   // exit code for this thread
 );

هذا الدالة تأخذ وسيط واحد وهو كود الانهاء ExitCode الذي تم الحصول عليه باستخدام الدالة GetExitCodeThread التي اشرنا لها سابقا .

هذه الدالة هي المفضلة لإنهاء الثريد , فعند استدعائها تحرر ذاكرة الستاك Stack المخصصة للثريد وتقوم بإنهائه , ايظا تقوم بتغيير حالة الثريد من القيمة STILL_ACTIVE الى قيمة كود الإنهاء ExitCode , كما انها تصرح "Invoke" لدالة Entry-Point المستدعاة من مكتبات الربط الديناميكي (DLLs) التي يكون الـ ThreadمرتبطAttatched بها ويتم استدعائها بقيمة تشير لفك ارتباط Detach الثريد من مكتبة الربط الديناميكي .

ايظا ان كان الثريد اخر ثريد في العملية Process التي يتبع لها الثريد فأنها سوف تنهي العملية Process تللك .

مثال على استخدامها :

DWORD exCode;
 GetExitCodeThread( ThreadHandle , &exCode );
 ExitThread( exCode );

:: ثانيا - الدالة TerminateThread والشكل العام لها كالإتي :

BOOL TerminateThread(
   HANDLE hThread,	// handle to the thread
   DWORD dwExitCode   // exit code for the thread
 );

هذه الدالة تأخذ وسيطين الوسيط الاول مقبض الثريد الـ Thread المراد إنهائه , والوسيط الثاني مؤشر لمتغير يستقبل قيمة كود الإنهاء ExitCode , وفي حالة نجاح الدالة ترجع بقيمة لا تساوي صفر nonzero , وفي حالة فشلها فإنها ترجع بالقيمة 0 .

عند استدعاء هذه الدالة فإنها تقوم بانهاء الـ Thread مباشرة دون تحرير ذاكرة الستاك Stack المخصصة للثريد , و تقوم بتغيير حالة الثريد من القيمة STILL_ACTIVE الى قيمة كود الإنهاء ExitCode , كما انها لا تعلم مكتبات الربط الديناميكة التي قد يكون الـ Thread مرتبط بها بحالة انهاء الثريد , وكما اشرنا سابقا تعتبر هذه الدالة خطرة ولا ينصح باستخدامها الا في اضيق الحدود.

ايظا ان كان الثريد اخر ثريد في العملية Process التي يتبع لها الثريد فأنها سوف تنهي العملية Process تللك .

مثال على استخدامها :

DWORD exCode;
 GetExitCodeThread( ThreadHandle,&exCode );
 TerminateThread( ThreadHandle , exCode );

::: ملاحظة هامة جدا :::

إنهاء الـ Thread لا يعني ضرورة حذفه من ذاكرة نظام التشغيل , ولكن يتم حذف الـ Thread ككائن موجد في ذاكرة النظام عند غلق اخر مقبض Handle له .

لغلق مقبض Handle لكائن Object يتم استخدام الدالة CloseHandle والشكل العام لها كالاتي :

BOOL CloseHandle(
   HANDLE hObject   // handle to object to close
 );

هذه الدالة تأخذ وسيط واحد هو مقبض الكائن المراد غلقه , وفي حالة نجاح الدالة ترجع بقيمة لا تساوي صفر nonzero , وفي حالة فشلها فإنها ترجع بالقيمة 0 .

مثال على استخدامها :

CloseHandle( ThreadHandle );

~~~ الخلاصة ~~~

إنهاء الـ Thread بطريقة صحيحة يتم في ثلاث خطوات هي :

1- الحصول على كود الانهاء باستخدام الدالة

2- إنهاء الـ Thread باحدى الدالتين او .

3- غلق مقبض الـ Thread باستخدام الدالة .

مثال على إنهاء الـ Thread بشكل صحيح :

DWORD exCode;
 GetExitCodeThread( ThreadHandle , &exCode );
 ExitThread( exCode );
 CloseHandle( ThreadHandle );

~~~ هل يجب إنهاء الـ Thread دائما ؟ ...

عند إنتهاء ورجوع الدالة التي يبدأ منها الـ Thread اي الدالة التي تمرر كوسيط لدالة الإنشاء " CreateThread " فإن نظام التشغيل Windows يستدعي تلقائيا الدالة ExitThread .

عموما يممكنا كما ذكرنا سابقا من انهاء الـ Thread في اي وقت .

~~~ ~~~ ~~~

هذه اهم الدوال المرتبطة بالثريد Thread وتوجد بضع دوال اخرى قليلة لم نتكلم عنها , ان دعت الحاجة ربما نتطرق لها لاحقا ان شاء الله تعالى .

اتمنى ان اكون افدتكم و لو بالقليل , مع اعتذر ي المسبق في حال وجود اي خطأ او تقصير, وللمشرفين صلاحية التعديل او التنسيق فضلا عن تصحيح اي خطأ يوجد .

والى دروس قادمة إن شاء الله تعالى اترككم في حفظ الله تعالى

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

مفهوم تعدد المسالك في نظام التشغيل Windows - الدرس الاول :

تم تعديل هذه المشاركة بواسطة بن العيد في 30 مايو 2009 في 20:09

while( "اسلام" )
{
 cout >> "الموت لبني صهيون" >> endl;
}
return "فلسطين";
#2

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

حقيقى موضوع جميل و شيق.

فى كذا سؤال محتاج اسألهم يختصوا بنفس الموضوع:

1 - هنفترض ان الـ Process يحتوى على 2 Thread اولهم الـ Main Thread و الثانى هنسميه X دلوقتى الإتنين بيعملوا سويا داخل الـ Process ماذا يحدث لو قمت بعمل Suspend للـ Main Thread من داخل الـ Thread X هل يتوقف عمل البرنامج ام يمكن فيما بعد عمل Resume له من خلال الـ Thread X.

2 - يمكن احضار الـ Exit Code للـ Thread الموجود داخل الـ Process من داخل اى Process اخر و عمل Terminate فكيف يمكن منع ذلك لهذا الـ Thread فقط و ليس للـ Process ككل.

3- فى بعض البرامج مثل Kaspersky يتم عمل حمايه على الـ Handle الخاص بالـ Process فكيف يمكن عمل هذه العمليه.

4- فى حال حدوث Terminate بشكل غير متوقع للـ Thread كيف يمكن المعرفة بحدوث ذلك.

و الله و لى التوفيق.

تم تعديل هذه المشاركة بواسطة Game Expert في 30 مايو 2009 في 10:07

مدونتي: C++ Tips and Tricks

#3

1- كل Thread منفصل عن الآخر ، يعني مثلا لو قمت بعمل Suspend للـThread الخاص بالـMain Dialog سيتم ايقاف التعامل مع الـGUI لكن ستظل الـThreads الباقية تعمل كل واحدة مهمتها ، ويمكن عمل Resume عن طريق ResumeThread .

2- يتم ذلك عن طريق اقتناص hook هذه الدوال وتحويل مجرى التنفيذ ، يتم ذلك في مستوى المستخدم user-mode او مستوى النظام kernel-mode وهو الاكثر ضماناً وخطورة بنفس الوقت ، وتوجد طرق مختلفة لفعل ذلك (IAT/SSDT/Inline/DKOM hooks) .

3- كاسبر وكل البرامج التي تستخدم Self-Defence تقوم بعمل hook على الدوال الخاصة بالـProcesses/Threads لمنع التلاعب في الكائنات الخاصة بها ، يتم ذلك غالبا من kernel-mode ، دوال الـNative التي يمكنك من خلالها الحصول على مقبض للـProcess/Thread هي NtOpenProcess/NtOpenThread/NtDuplicateObject .

4- هناك طرق كثيرة في ذهني لكن هل تقصد معرفة ذلك بعد عملية الـRundown ام قبلها (لغرض المنع) ؟

mov eax, dword ptr ds:[0xffdf0308]

jmp dword ptr [eax+0xfc]

#4

اخى GamingMasteR جزاك الله خيرا على اجابتك

بالنسبه لإجابة السؤال الأول فقد فهمتها تماما.

بالنسبه لإجابتك للسؤال الثانى و الثالث :hmm: فلم استعوبها حيث ان موضوع الـ hooks من المواضيع التى لا افهمها فإذا كان لديك كتاب معين يمكننى قرائته فى هذا الموضوع فأكن شاكرا لك (طبعا انا شاكر لك بدون شئ يكفى اجابتك للأسئله).

بالنسبه للسؤال الرابع انا اقصد بعد حدوث انهاء للثريد بشكل غير متوقع كيف استطيع معرفة سبب توفقه و فى اى مرحله.

و شكرا لك كثيرا :blush:

مدونتي: C++ Tips and Tricks

#5
اقتباس
فإذا كان لديك كتاب معين يمكننى قرائته فى هذا الموضوع فأكن شاكرا لك (طبعا انا شاكر لك بدون شئ يكفى اجابتك للأسئله).

ابحث فقط في CodeProject عن كلمة Hook او اي موقع للهندسة العكسية .

اقتباس
بالنسبه للسؤال الرابع انا اقصد بعد حدوث انهاء للثريد بشكل غير متوقع كيف استطيع معرفة سبب توفقه و فى اى مرحله.

OpenThread -> WaitForSingleObject

توجد طرق من الكرنل مثل الـCallbacks التي يدعمها النظام : PsSetCreateThreadNotifyRoutine

mov eax, dword ptr ds:[0xffdf0308]

jmp dword ptr [eax+0xfc]

#6

بســم الله الـرحمــن الرحيــم

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

اهلا اخي الكريم

السؤال الاول :

حتى تفهم الجواب يجب ان تعلم ان البرنامج و الذي سنطلق عليه هنا العملية او Process يجب ان يحتوي على الاقل على Thread واحد

فمثلا هذا البرنامج "Process" يحتوي على Thread واحد هو Process' Thread

#inclue <iostream>
using namespace std;

int main()
{
cout << "Hello ArabTeam" << endl;
return 0;
}

لننظر الان للمثال الاتي

#inclue <iostream>
using namespace std;

void DummyFunction (void)
{
cout << "This is Thread's Function !" << endl;

int main()
{
HANDLE ThreadHandle;
DWORD threadID;

cout << "main() belongs to Procces's Thread !" << endl;
ThreadhHandle = CreateThread(NULL,0,(LPTHREAD_START_ROUTINE)DummyFunction,0,0,&threadID);
Closehandle(threadhandle);

return 0;
}

هذا البرنامج به عدد 2 Threads الاول ثريد العملية Process's Thread والاخر منشأ من الدالة CreateTread .

لنجيب سؤالك لنتبع سير البرنامج :

- البرنامج بدأ بـ Thread واحد ومنه سيتم تنفيذ الدالة main .

- بدأ تنفيذ الدالة main والتي ستقوم بطباعة "! main() belongs to Procces's Thread"

- ثم ستنتقل الدالة main لتنفيذ التعليمة الموالية وهي استدعاء دالة إنشاء الثريد CreateThread .

"الان تم إنشاء Thread جديد ولكن Process او بالاحرى الدالة main لن تعيره اهتماما ولن تنتظر الاجراءات او التعليمات التي سيقوم بهاهذا الـ Thread بل انها في الحقيقة لم تتعامل مع هذا الـ Thread ابدأ بل انها

تعاملت مع نظام التشغيل عند أستدعاء الدالة CreateThread وسواء كان الثريد يعمل او في الوضع Suspend ستواصل الدالة main تنفيذ باقي تعليمات البرنامج" .

- ستنتقل الان الدالة main للتعليمة التالية و هي استدعاء الدالة CloseHandle وتنفذ ها ثم تنتقل للتعليمة البرمجية التالية في تسلسل البرنامج .

- ستنتقل الدالة main الان الى السطر البرمجي return 0 اي إنهاء البرنامج , وحيث اننا لم نطلب في برنامجنا من الدالة main بانتظار الـ Thread الذي انشئناه باستخدام دوال الانتظار مثل الدالة WaitForSingleObject فعليه ستقوم الدالة main بإنهاء البرنامج دون انتظار رجوع الـ Thread .

ارجو ان تكون الفكرة وضحت لك , بالمختصر Process's Thread تنشئه العملية فستقوم بالتالي بالتكفل به , اما الثريد التي ينشئها المبرمج فعليه ان يتكفل بها , فكر بها بنفس الطريقة التي نتعامل مع الذاكرة بها فالذاكرة التي يديرها البرنامج هو المسئول عنها اما الذاكرة التي يحجزها المبرمج باستخدام new فهو المسؤل عن التعامل معها و تحريرها .

الجزء الاخر من السؤال المتعلق بايقاف و استئناف الـ Thread : لنتأمل هذه الكلمة العربية " مقبض " وجذرها " قبض " تعطي انطباعا على التحكم و السيطرة , و مادام عندك المقبض ولديك الصلاحيات فإنك تستطيع عمل ما تشاء .

اي Thread تملك مقبضه يمكن تعليق عمله و استئنافه في اي وقت , اما سير عمل البرنامج سيكون بحسب حالة الـ Threads فالثريد Thread العامل سيستمر في العمل لحين انتهاء تعليمات دالة الثريد ThearProc او صدور امر بتعليق عمله او انتهائه والـ Thread الذي في وضع التعليق سيضل متوقفا عن العمل لحين استئنافه باستدعاء الدالة ResumeThread .

يقولون بالمثال يتضح المقال فإاليك هذا المثال :

#include <windows.h>

#include <iostream>
using namespace std;

HANDLE hThreads[2];

void FirstThread()
{
if ( SuspendThread(hThreads[0]) == 0 ){
  cout << "FirstThread says : Oobs ,I Suspended myself !" << endl; // this Line WILL 

NOT BE PRINTED,Beacause ...


   // the thread will be stoped as soon as the ThreadSuspend() Called 
}			

   // So, it will be PRINTED NEXT TIME the thread resumed .

for ( int m = 0; m < 10; m++ ){
  cout << "FirstThread says : Loop no: " << m << endl;
  Sleep(1000);

}
}


void SecondThread()
{
Sleep(200);
ResumeThread( hThreads[0] );
Sleep(3000);

if ( SuspendThread(hThreads[0]) == 0){
  cout << "SecondThread says : I Suspended FirstThread !" << endl;
}

Sleep(3000);
cout << "SecondThread says : Let's Resume FirstThread !" << endl << endl;
ResumeThread( hThreads[0] );
}


int main()
{
cout << "main() says : There is a Thread running already !" << endl;
cout << "main() says : I run inside Process's Thread; and guess what!" << endl;
cout << "main() says : I will keep running until Program's end" << endl << endl; 

DWORD ThreadID[2]; // or ThreadID

hThreads[0] = CreateThread(NULL,0,(LPTHREAD_START_ROUTINE)FirstThread,0,0,&ThreadID[0]);
hThreads[1] = CreateThread(NULL,0,(LPTHREAD_START_ROUTINE)SecondThread,0,0,&ThreadID[1]);

if ( SuspendThread(hThreads[1]) == 0){
  Sleep(200); // delay for cout
  cout << "main() says : I Suspended SecondThread !" << endl << endl;
}

Sleep(3000);

if ( ResumeThread(hThreads[1]) <= 1 )
  cout << "main() says : I Resumed SecondThread !" << endl << endl;


WaitForMultipleObjects(2, hThreads, TRUE, INFINITE);
CloseHandle(hThreads);
cout << "main() says : I finshed executing all the instructions of the Process's 

Thread..END...!" << endl;

return 0;
cout << "main() COULD NOT SAY : Program Ended ...!" << endl; // this line WILL NOT EXECUTE 
}

~~~ ~~~ ~~~

ان فهمت السابق فسأوجه لك سؤال وهو كيف يمكن تعليق و استئناف " التحكم " في الـ Process's Thread من ثريد اخر ؟

للتسهيل في المثال السابق كيف يمكنني تعليق واستئناف عمل الدالة main عن طريق الدالة FirstThread او الدلة SecondThread ؟

Tip - ابحث عن استخدام الدالة GetCurrentThread ومفهوم المقبض pseudohandle وكيفية الاستفادة منه .

~~~ ~~~ ~~~

السؤال الثاني و الثالث متشابهان بقدر تشابه Thread و Process

لننظر ماذا تقول الت MSDN حول الدالة TerminateThread .

A thread cannot protect itself against TerminateThread, other than by controlling access to its

handles. The thread handle returned by the CreateThread and CreateProcess functions has

THREAD_TERMINATE access, so any caller holding one of these handles can terminate your thread.

يتضح لنا من المقال ان الـ Thread لا يستطيع حماية نفسه من الانهاء مادام مقبضه متوفرا , و اي عملية تستدعى هذا الـ Handle قادرة على إنهاء الثريد طالما لمقبض هذا الـ Thread الصلاحية في الوصول لخصائص الانهاء THREAD_TERMINATE وهي الخصائص التي تعود بها دالة إنشاء الـ Thread وهي الدالة CreateThread ودالة إنشاء الـ Process وهي الدالة CreateProcess . الاجراء الوحيد لحماية الـ Thread يكمن في التحكم في صلاحيات الوصول لمقبضه وهذا يتم عن طريق التركيبة SECURITY_ATTRIBUTES وهي الوسيط الاول الذي يتم تمريره للدالة CreateThread وكذلك للدالة Createprocess .

بصراحة لم يسبق لي تحديد خصائص الوصول والتعامل مع هذه التركيبة فلم يمضي على تعلمي لـ ++C بضعة اشهر وعلى فترات متقطعة من مواقع الانترنت .

عموما إبحث عن Access Control والقي نظرة على التركيبة SECURITY_ATTRIBUTES فقد تجد بعض المعلومات , ومن جهتي ان توصلت لشيء سأحاول شرحه ووضعه هنا إن شاء الله .

~~~ ~~~ ~~~

السؤال الرابع : لنرى ماذا تقول الـ MSDN

TerminateThread is a dangerous function that should only be used in the most extreme cases. You

should call TerminateThread only if you know exactly what the target thread is doing, and you

control all of the code that the target thread could possibly be running at the time of the

termination. For example, TerminateThread can result in the following problems:

يتضح لنا من المقال خطورة هذه الدالة والتشديد على استعمالها في اضيق الحدود , كذلك على المبرمج تولي كافة اجزاء الكود الذي يشغله الـ Thread وقت إنهائه , كما ان الدالة TerminatedThread لا تعلم المكتبات الديناميكية المرتبطة معها بإنهاء الـ Thread , ومع هذا فالدالة تغير حالة الـ Thread من القيمة STILL_ACTIVE الى قيمة ExitCode.

ومع سهولة الاستعلام عن حالة الـ Thread باستخدام الدالة GetExitCodeThread فاذا لم يتم إنهاء الـ Thread فسترجع حالة الثريد بالقيمة STILL_ACTIVE الا انه لا يمكن استخدامه لتقرير اذا ما كان الـ Thread مازال نشطا ام لا , لإنه ببساطة ان حالة الثريد ستأخذ اما قيمة الدالة ExitThread او الدالة TerminateThread واما القيمة الراجعة من الدالة التي يبدأ بها الثريد ThreadProc واما قيمة الخروج لثريد العملية Process's Thread وبالتالي لن يمكن التمميز بينها.

ايظا باستخدام الدالة WaitForSingleObject واعطائها زمن انتظار 0 اي الكشف عن حالة الـ Thread هل في حالة Signaled او NonSignaled لن يعطي كفاءة في التحقق من حالة الثريد عند استخدام الدالة TerminateThread وطريقة استخدام الدالة WaitForSingleObject للكشف عن حالة الثريد signaled او nonsignaled تتم كالاتي :

DWWORD retVal = WaitForSingleObject( hThreads[0], 0);
if ( retVal == WAIT_OBJECT_0 ){
  // Thread signaled  --- // Do something
}

كما قلنا الدالة CreateThread تعتبر خطرة و تعمل على حدوث Aggressive ShutDown للثريد , الافضل طبعا استخدام الدالة ExitThread فهي تؤدي لعمل Termination او ShutDown للثريد بطريقة سليمة و امنة .عموما توجد تقنيات عديدة للتأكد من حالة الثريد منها استخدام الاحداث Event وسنتطرق لها عند الحديث على مزامنة المسالك او Threads Synchronization .

ربما يفيدنا إيظا الاخ GamingMaster بإحدى التقنيات التي اشار لها .

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

تم تعديل هذه المشاركة بواسطة HaTy في 30 مايو 2009 في 22:32

while( "اسلام" )
{
 cout >> "الموت لبني صهيون" >> endl;
}
return "فلسطين";
#7

شكرا جزيلا لكما GamingMasteR و HaTy جزاكما الله خيرا و جعل اعمالكم فى ميزان حسناتكم

وفقنى الله و اياكم لما يحبه و يرضاه.

تم تعديل هذه المشاركة بواسطة Game Expert في 31 مايو 2009 في 00:16

مدونتي: C++ Tips and Tricks

مواضيع مشابهة