السلام عليكم
بسم الله الرحمن الرحيم
لقد تحدث في مقالات سابقة عن تعدد المسالك ولكن هذه المرة سوف أتحدث عن أمور لم أتحدث عنها مسبقا تساعدك في بناء برامج متعددة المسالك تتميز بأداء عالي وسطور أقل
قبل أن أتحدث عن ماهو جديد Qt في تعدد المسالك "Qt4.4" فإنه من الأفضل القاء نظرة على مصطلحات تجدها كثيرا في QtAssistant وهي reentrant functions و thread-safe function و reentrant/safethread classes وماذا يعني إستخدام هذه الدوال أو الفئات داخل برامج متعددة المسالك :-
Reentrant functions
1- يتم استدعائها في نفس الوقت من قبل عدة مسالك
2- كل استدعاء يشير الى بيانات مختلفة (حتى لايحدث تضارب في المصادر وإلا فإن النظام يرسل رسالة خطأ).
Thread-safe functions
1- يتم استدعائها في نفس الوقت من قبل عدة مسالك .
2- كل إستدعاء يشير الى بيانات مختلفة .
3- في حال المشاركة في مصار البيانات فإنه يتم الوصول لها بشكل متسلسل .
Reentrant or thread-safe functions
جميع الدوال بإستثناء الساكنة إما ان تكون reentrant functions او thread-safe functions .
لاحظ أن thread-safe تعتمد دوالها على أحد مبادئ التزامن لتلافي الإشتراك في مصدر واحد للبيانات في نفس الوقت .
ملاحظة قبل البدئ في المقال فجميع ما سوف يتم شرحه يتوفر مع إصدارات Qt4.4 فإذا كنت تملك إصدار سبق هذا الإصدار فإن الأمثلة لن تعمل معك .
1-إدارة مجموعة مسالك بإستخدام الفئة QthreadPool .
في جميع ماشرحته مسبقا كنا ندير كل مسلك على حدى ولكن هذه المرة سوف نرى كيف بإمكاننا إنشاء مجموعة مسالك تدار سويا بشكل سلس ومبسط .
فيما سبق كنا نرث الفئة Qthread ثم نعيد تعريف الدالة run لكي ننشئ مسلك جديد ولكن هذه المرة سوف نرث من الفئة QRunnable وتعيد تعريف الدالة run وهو ماسوف يتم تنفيذ جميع مابداخله ويمكنك أيضا تحديد فيما أردت أن تسمح ل QthreadPool بحذفها بعد تنفيذ ما بداخلها أو لا بإستدعاء الدالة setAutoDelete حيث القيمة true تسمح لها بحذفها و false لا تسمح .
بعدما قمنا بإنشاء فئة جديدة ترث من الفئة Qrunnable فإنه بإمكانك الآن إضافتها الى QthreadPool لكي يجعلها تعمل في مسلك فريد يقوم هو بإدارته وذلك بإستدعاء الدالة start او tryStart حيث تدخل في وسيطها متغير الفئة التي قمت بإنشائها بالوراثة من Qrunnable .
أنظر المثال :-
1- #include <QtGui>
2- class myThread:public QRunnable{
3- public:
4- myThread(QString s);
5- void run();
6- QString st;
7- };
8- myThread::myThread(QString s):st(s){
9- }
10- void myThread::run(){
11- int i=3;
12- while(i){
13- qDebug()<<st;
14- i--;
15- }
16- }
17- int main(int argc, char *argv[])
18- {
19- QApplication a(argc, argv);
20- myThread* firstThread=new myThread("first");
21- myThread* secondThread=new myThread("second");
22- QThreadPool::globalInstance()->setMaxThreadCount(1);
23- QThreadPool::globalInstance()->start(firstThread);
24- QThreadPool::globalInstance()->start(secondThread);
25- return a.exec();
26- }في الأسطر من 2 الى 15 قمنا بإنشاء الفئة myThread الت ترث من QRunnable ثم قمنا بإعادة تعريف الدالة run ثم وفي الدالة main والأسطر من 20 الى 25 قمنا ببالتصريح عن متغيرات الفئة التي أنشأناها ثم أستخدمنا الدالة setMaxThreadCount وهي أحد طرق إدارة تعدد المسالك وذلك بالسماح لعدد معين من المسالك بالعمل في وقت واحد وفي حال تم الإنتهاء من أحدها أو جميعها يتم أخذ مجموعة أخرى من المسالك لتنفيذها في حال كان ما بداخل الوسيط 0 او -1 فإن عدد المسالك التي تعمل دفعة واحدة يساوي عدد المسالك التي يمكن إنشاءها في المعالجات الرئيسية والتي يمكن معرفتها بإستدعاء الدالة QThread::idealThreadCount ثم أخيرا قمنا بتنفيذ العمليات بإستخدام الدالة start فيصبح ناتج العملية كالتالي :-
First First First Second Second Second
لاحظ أن النواتج لم تكن مشعثرة بل بدأ بتنفيذ المسلك الأول وحين أنتهى منه بدأ بتنفيذ المسلك الثاني وربما يبدأ بالمسلك الثاني وحين ينتهي منه ينتقل للأول فهو سماح عشوائي .
حالة الإنتظار QwaitCondition
توفر لك طريقة أخرى للتزامن بالإضافة لطرق سابقة تحدث عنها مقال سابق
ولكن التزامن هنا على شكل إنتظار مؤقت وفك الإنتظار المؤقت بالإضافة أن هذا الإنتظار سوف يسمح لجميع ما أقفل عليه ال mutex قبل تنفيذه بالتنفيذ ويبقى الذي دخل mutex وأقفله ينتظر ليقفل على الآخرين فيما لو أنتهى وقت الإنتظار ويبدأ هو بتنفيذ مابداخله بمعنى آخر هو عكس مؤقت لعملية mutex أنظر المثال :-
1- #include <QtGui>
2- QMutex mutex;
3- QWaitCondition wc;
4- class myThread:public QThread{
5- public:
6- myThread(QString s);
7- void run();
8- QString st;
9- };
10- myThread::myThread(QString s):st(s){
11- }
12- void myThread::run(){
13- int i=5;
14- while(--i){
15- mutex.lock();
16- wc.wait(&mutex,1000);
17- qDebug()<<st;
18- mutex.unlock();
19- }
20- }
21- class myThread2:public QThread{
22- public:
23- myThread2(QString s);
24- void run();
25- QString st;
26- };
27- myThread2::myThread2(QString s):st(s){
28- }
29- void myThread2::run(){
30- int i=5;
31- while(--i){
32- mutex.lock();
33- qDebug()<<st;
34- mutex.unlock();
35- }
36- }
37- int main(int argc, char *argv[])
38- {
39- QApplication a(argc, argv);
40- myThread* firstThread=new myThread("first");
41- myThread2* secondThread=new myThread2("second");
42- myThread2* second2Thread=new myThread2("second2");
43- firstThread->start();
44- secondThread->start();
45- second2Thread->start();
46- return a.exec();
47- }المخرجات
"second" "second2" "second" "second2" "second" "second2" "second" "second2" "first" "first" "first" "first"
لاحظ بأن ما يفترض أن ينفذ أولا أصبح ضحية هذه الفئة
ويمكنك أيضا القيام بإيقاف الإنتظار بإستخدام أحد الدالتين wakeOne او wakeAll ويشترط عند استخدام الدالة wait أن يكون ال mutex المرسل له كوسيط مقفل عليه .

