شاركنا بعملوماتك
#عائله بايثون
مفهوم تعدد المهام
عندما تستخدم هاتفك أو حاسوبك, ترى أنه يمكنك تشغيل عدة برامج مع بعض في وقت واحد, كل برنامج شغال في الذاكرة يعتبر Process, فمثلاً إذا قمت بتشغيل خمسة برامج مع بعض فهذا يعني أن نظام التشغيل ينظم عمل خمسة Processes مع بعض. آلية تشغيل عدة برامج مع بعض تسمى Multiprocessing.
من جهة اخرى, في البرنامج الواحد يمكنك تنفيذ عدة أوامر مع بعض و جعل المستخدم يشعر كأنها تتنفذ في وقت واحد, فمثلاً في حال كنت تلعب لعبة مثل لعبة كرة القدم, تجد أنه هناك عدة أشياء تحدث في وقت واحد, فمثلاُ عند تشغيل اللعبة تسمع عدة أصوات ( مثل أغنية حماسية, صوت المعلق, صوت المشجعين, صوت صفارة الحكم في حال وقع خطأ إلخ.. ), بالإضافة إلى أنه يمكنك تحريك اللاعب و مشاهدة توقيت المبارة و الكثير من التفاصيل الأخرى التي تحدث كلها في نفس الوقت لتصنع لك لعبة رائعة. هذه الآلية تسمى Multithreading, لأن كل جزء شغال في البرنامج يكون عبارة عن مجموعة أوامر موضوعة بداخل Thread خاص.
إذاً نستخدم آلية Multithreading لجعل البرنامج قادر على تنفيذ عدة أوامر مع بعض و كأنها تتنفذ في وقت واحد, و هذا ما سنتعلمه في هذا الدرس.
عندما تستخدم هاتفك أو حاسوبك, ترى أنه يمكنك تشغيل عدة برامج مع بعض في وقت واحد, كل برنامج شغال في الذاكرة يعتبر Process, فمثلاً إذا قمت بتشغيل خمسة برامج مع بعض فهذا يعني أن نظام التشغيل ينظم عمل خمسة Processes مع بعض. آلية تشغيل عدة برامج مع بعض تسمى Multiprocessing.
من جهة اخرى, في البرنامج الواحد يمكنك تنفيذ عدة أوامر مع بعض و جعل المستخدم يشعر كأنها تتنفذ في وقت واحد, فمثلاً في حال كنت تلعب لعبة مثل لعبة كرة القدم, تجد أنه هناك عدة أشياء تحدث في وقت واحد, فمثلاُ عند تشغيل اللعبة تسمع عدة أصوات ( مثل أغنية حماسية, صوت المعلق, صوت المشجعين, صوت صفارة الحكم في حال وقع خطأ إلخ.. ), بالإضافة إلى أنه يمكنك تحريك اللاعب و مشاهدة توقيت المبارة و الكثير من التفاصيل الأخرى التي تحدث كلها في نفس الوقت لتصنع لك لعبة رائعة. هذه الآلية تسمى Multithreading, لأن كل جزء شغال في البرنامج يكون عبارة عن مجموعة أوامر موضوعة بداخل Thread خاص.
إذاً نستخدم آلية Multithreading لجعل البرنامج قادر على تنفيذ عدة أوامر مع بعض و كأنها تتنفذ في وقت واحد, و هذا ما سنتعلمه في هذا الدرس.
أهمية تعدد المهام
- جعل المستخدم قادر على تنفيذ عدة عمليات مع بعض في نفس الوقت.
- جعل تصميم التطبيقات أجمل و إضافة مؤثرات فيه.
- كل Thread تقوم بتشغيله, يعمل بشكل منعزل عن باقي الأوامر الموجودة في البرنامج, و بالتالي فإنه في حال وقوع أي خطأ في الـ Thread فإنه لن يؤثر على باقي الأوامر الموجود في البرنامج, كما أنه لا يؤثر على أي Thread آخر شغال في البرنامج.
- جعل المستخدم قادر على تنفيذ عدة عمليات مع بعض في نفس الوقت.
- جعل تصميم التطبيقات أجمل و إضافة مؤثرات فيه.
- كل Thread تقوم بتشغيله, يعمل بشكل منعزل عن باقي الأوامر الموجودة في البرنامج, و بالتالي فإنه في حال وقوع أي خطأ في الـ Thread فإنه لن يؤثر على باقي الأوامر الموجود في البرنامج, كما أنه لا يؤثر على أي Thread آخر شغال في البرنامج.
طريقة إنشاء Thread
في البداية, إبتداءاً من الإصدار 2.4 في بايثون, أصبح الموديول
هو الموديول الذي يستخدم لإنشاء Threads.
هذا الموديول عبارة عن موديول جاهز في بايثون, و هو يحتوي على الكلاس الذي يستخدم لبناء Thread و التحكم به.
في البداية, إبتداءاً من الإصدار 2.4 في بايثون, أصبح الموديول
هو الموديول الذي يستخدم لإنشاء Threads.هذا الموديول عبارة عن موديول جاهز في بايثون, و هو يحتوي على الكلاس الذي يستخدم لبناء Thread و التحكم به.
إنتبه
قبل هذا الموديول كان يوجد موديول إسمه
يستخدم لهذا الغرض أيضاً لكنه لم يعد يستخدم الآن و سيتم إلغاؤه مستقبلاً.
لذلك ننصح بعدم استخدام الموديول أو الإعتماد عليه.
قبل هذا الموديول كان يوجد موديول إسمه
يستخدم لهذا الغرض أيضاً لكنه لم يعد يستخدم الآن و سيتم إلغاؤه مستقبلاً.لذلك ننصح بعدم استخدام الموديول أو الإعتماد عليه.
خطوات بناء Thread
- يجب تضمين الموديول .
- بناء كلاس يرث من الكلاس Override لدالة إسمها و فيها تضع الأوامر التي تريدها أن تتنفذ عندما يتم تشغيل ,الـ Thread.
و تفعل
- يجب تضمين الموديول .
- بناء كلاس يرث من الكلاس Override لدالة إسمها و فيها تضع الأوامر التي تريدها أن تتنفذ عندما يتم تشغيل ,الـ Thread. و تفعل
خطوات تشغيل Thread
- يجب إنشاء كائن من الكلاس الذي يرث من الكلاس .
- من هذا الكائن, نقوم باستدعاء دالة جاهزة إسمها
و التي ستقوم بشكل تلقائي باستدعاء الدالة و تنفيذ الأوامر الموضوعة فيها.
في المثال التالي قمنا بإنشاء كلاس إسمه
يرث من الكلاس .في الدالة
الخاصة بهذا الكلاس, قمنا بتعريف خاصية إسمها لأننا ننوي إعطاء إسم لكل كائن ننشئه من هذا الكلاس.
كما أننا جعلناها تستدعي الدالة الموجودة في الكلاس لنكون قادرين على معاملة أي كائن ننشئه من هذا الكلاس كـ Thread.بعدها فعلنا Override للدالة
بهدف أن تطبع الإسم الذي نضعه في الخاصية ثلاث مرات عندما يتم تشغيله.في الأخير, قمنا بإنشاء كائنين من هذا الكلاس و تشغيلهما في وقت واحد.
- يجب إنشاء كائن من الكلاس الذي يرث من الكلاس .
- من هذا الكائن, نقوم باستدعاء دالة جاهزة إسمها و التي ستقوم بشكل تلقائي باستدعاء الدالة و تنفيذ الأوامر الموضوعة فيها.
في المثال التالي قمنا بإنشاء كلاس إسمه
يرث من الكلاس .في الدالة
الخاصة بهذا الكلاس, قمنا بتعريف خاصية إسمها لأننا ننوي إعطاء إسم لكل كائن ننشئه من هذا الكلاس.كما أننا جعلناها تستدعي الدالة الموجودة في الكلاس لنكون قادرين على معاملة أي كائن ننشئه من هذا الكلاس كـ Thread.
بعدها فعلنا Override للدالة
بهدف أن تطبع الإسم الذي نضعه في الخاصية ثلاث مرات عندما يتم تشغيله.في الأخير, قمنا بإنشاء كائنين من هذا الكلاس و تشغيلهما في وقت واحد.
مثال
سنحصل على نتيجة تشبه النتيجة التالية عند تشغيل الملف
.
سنحصل على نتيجة تشبه النتيجة التالية عند تشغيل الملف
.ملاحظة
في المثال السابق, يمكنك حذف السطر 12 لأن الكلاس
ورث أيضاً خاصية إسمها من الكلاس .
لهذا, يمكنك تمرير الإسم مباشرةً إلى الخاصية التي ورثها الكلاس بدل تعريفها فيه من جديد.
في المثال السابق, يمكنك حذف السطر 12 لأن الكلاس
ورث أيضاً خاصية إسمها من الكلاس .لهذا, يمكنك تمرير الإسم مباشرةً إلى الخاصية التي ورثها الكلاس بدل تعريفها فيه من جديد.
معلومة تقنية
عند تشغيل أكثر من Thread في وقت واحد, لا يمكنك ضمان أو تحديد أي Thread سيتنفذ أو ينتهي قبل الآخر.
السبب في ذلك أن معالج الحاسوب ( CPU ) سيقوم بإرسال كل Thread قمت بتشغيله إلى نواة ( Core ) حتى ينفذهم لك في وقت واحد.
و منطقياً, النواة التي عليها ضغط أقل ستنتهي من تنفيذ أوامر الـ Thread بشكل أسرع.
لهذا السبب, إذا قمت بتشغيل المثال السابق أكثر من مرة, ستجد أنك في كل مرة ستحصل على نتيجة مختلفة عند التشغيل.
عند تشغيل أكثر من Thread في وقت واحد, لا يمكنك ضمان أو تحديد أي Thread سيتنفذ أو ينتهي قبل الآخر.
السبب في ذلك أن معالج الحاسوب ( CPU ) سيقوم بإرسال كل Thread قمت بتشغيله إلى نواة ( Core ) حتى ينفذهم لك في وقت واحد.
و منطقياً, النواة التي عليها ضغط أقل ستنتهي من تنفيذ أوامر الـ Thread بشكل أسرع.
لهذا السبب, إذا قمت بتشغيل المثال السابق أكثر من مرة, ستجد أنك في كل مرة ستحصل على نتيجة مختلفة عند التشغيل.
مفهوم الـ Main Thread
في بايثون, كل كود يتنفذ في البرنامج, فإنه حتماً يتنفذ بداخل Thread واحد على الأقل.
أي حتى لو لم تقم بوضع الكود بداخل Thread فإنه سيتم وضعه في Thread.
و بالتالي في حال قمت بتشغيل Thread في البرنامج, فهذا يعني أن البرنامج حالياً يعمل فيه إثنين Threads و ليس Thread واحد.
في المثال التالي, قمنا بطباعة عدد الـ Threads الذين يتنفذون حالياً علماً بأننا لم نقم بإنشاء أي كائن من كلاس يرث من الكلاس Thread.
في بايثون, كل كود يتنفذ في البرنامج, فإنه حتماً يتنفذ بداخل Thread واحد على الأقل.
أي حتى لو لم تقم بوضع الكود بداخل Thread فإنه سيتم وضعه في Thread.
و بالتالي في حال قمت بتشغيل Thread في البرنامج, فهذا يعني أن البرنامج حالياً يعمل فيه إثنين Threads و ليس Thread واحد.
في المثال التالي, قمنا بطباعة عدد الـ Threads الذين يتنفذون حالياً علماً بأننا لم نقم بإنشاء أي كائن من كلاس يرث من الكلاس Thread.
مثال
سنحصل على نتيجة تشبه النتيجة التالية عند تشغيل الملف
.نلاحظ أن عدد الـ Threads الذين يتنفذون حالياً هو 1, و هذا يثبت أن الكود الأساسي في البرنامج يتم وضعه في Thread.
كما أن إسم الـ Thread الأساسي و إسم الـ Thread الذي يتنفذ حالياً هو
.
سنحصل على نتيجة تشبه النتيجة التالية عند تشغيل الملف
.نلاحظ أن عدد الـ Threads الذين يتنفذون حالياً هو 1, و هذا يثبت أن الكود الأساسي في البرنامج يتم وضعه في Thread.
كما أن إسم الـ Thread الأساسي و إسم الـ Thread الذي يتنفذ حالياً هو
.دوال الموديول
الجدول التالي يحتوي على دوال الموديول
الأكثر إستخداماً و التي سبق أن استخدمناها في الأمثلة السابقة.إسم الدالة مع تعريفها 1 ترجع عدد الـ Threads الذين يتنفذون في الوقت الحالي الذي تم فيه إستدعاءها. 2 ترجع كائن الـ الأساسي في البرنامج.
معلومة: الـ الأساسي في البرنامج هو أول Thread بدأ مفسر لغة بايثون بتنفيذ الأوامر الموضوعة فيه. 3 ترجع كائن الـ الذي يتنفذ في الوقت الحالي الذي تم فيه إستدعاءها. 4 ترجع كائن يحتوي على كل كائن يتنفذ في الوقت الحالي الذي تم فيه إستدعاءها.
ملاحظة: لا ترجع أي تم إيقافه أو لم يتم تشغيله من الأساس.
الجدول التالي يحتوي على دوال الموديول
الأكثر إستخداماً و التي سبق أن استخدمناها في الأمثلة السابقة.إسم الدالة مع تعريفها | |
---|---|
1 | ترجع عدد الـ Threads الذين يتنفذون في الوقت الحالي الذي تم فيه إستدعاءها. |
2 | ترجع كائن الـ الأساسي في البرنامج. معلومة: الـ الأساسي في البرنامج هو أول Thread بدأ مفسر لغة بايثون بتنفيذ الأوامر الموضوعة فيه. |
3 | ترجع كائن الـ الذي يتنفذ في الوقت الحالي الذي تم فيه إستدعاءها. |
4 | ترجع كائن يحتوي على كل كائن يتنفذ في الوقت الحالي الذي تم فيه إستدعاءها. ملاحظة: لا ترجع أي تم إيقافه أو لم يتم تشغيله من الأساس. |
خصائص و دوال الكلاس
الجدول التالي يحتوي على دوال الكلاس
الأكثر إستخداماً و التي سبق أن استخدمناها في الأمثلة السابقة.إسم الدالة مع تعريفها 1 نفعل لها Override لنضع فيها الأوامر التي نريدها أن تتنفذ عند تشغيل كائن الـ . 2 تستخدم لتشغيل كائن الـ الذي قام باستدعائها.
فعلياً, الدالة تقوم فقط باستدعاء الدالة لتنفيذ الأوامر الموضوعة فيها.
إنتبه: لا يمكن تشغيل نفس كائن الـ أكثر من مرة. أي لا يمكن إستدعاء الدالة أكثر من مرة من نفس كائن الـ .
في حال تم استدعاء الدالة مرتين من نفس كائن الـ فإنها ترمي الإستثناء RuntimeError. 3 ترجع طالما أن كائن الـ لم ينتهي تنفيذه بعد حتى لو كان لم يتم البدء بتنفيذه أصلاً.
غير ذلك ترجع . 4 تجعل كائن الـ الذي قام باستدعائها ينتظر إلى أن ينتهي تنفيذ الـ الذي يعمل قبله قبل أن يبدأ هو بتنفيذ الأوامر الموجودة فيه.
في حال أردت تأخير تنفيذ الـ الذي سيعمل عند إنتهاء تنفيذ الـ لمدة محددة فيمكنك تمرير رقم مكان الباراميتر يمثل هذه المدة, مع الإشارة إلى أن الرقم الذي تمرره يمثل مدة الإنتظار بالثواني. فمثلاً إذا قمت بتمرير الرقم 1.5 فهذا يعني أنك تقصد ثانية و نصف.
معلومة تقنية
هذه الدالة مهمة عند الحاجة لجعل الـ Threads يتنفذوا الواحد تلو الآخر بدل أن يتنفذوا في وقت واحد و يسببوا مشكلة يقال لها Deadlock.
و لا تقلق إذا لم تكن تعرف المقصود من مصطلح Deadlock الآن لأنك ستفهمها لاحقاً من الأمثلة.
في حال وجد مترجم لغة بايثون أن عندما سيتم تشغيل الـ سيحدث Deadlock, سترمي الإستثناء RuntimeError.
الجدول التالي يحتوي على خصائص الكلاس
.إسم الخاصية مع تعريفها 1 ترجع الإسم الذي تم إعطاؤه لكائن الـ الذي قام باستدعائها.
فعلياً, عند إنشاء كائن من الكلاس الذي يرث من الكلاس يمكنك تمرير الإسم الذي تريد إعطاؤه له لحظة إنشاءه. 2 ترجع رقم التعرفة ( ID ) الذي يتم توليده بشكل عشوائي و إعطائه لكائن الـ الذي قام باستدعائها. 3 هذه الخاصية يمكن الإستفادة منها في حال كنت تنوي تشغيل Thread من داخل Thread آخر.
إذا مررت لها القيمة قبل أن تقوم بتشغيل الـ Thread الآخر, سيفهم مترجم لغة بايثون أنك تنوي إيقاف الـ Thread الآخر بشكل تلقائي عندما يتوقف الـ Thread الذي قام باستدعائه في الأساس. كما أنه في حال كان الـ Thread الآخر متصل بملف, بقاعدة بيانات, أو بالإنترنت إلخ.. فإن إغلاقه بشكل مفاجئ لا يضمن أن يتم إغلاق الإتصالات التي كان يجريها.
في حال وجد مترجم لغة بايثون أن عندما سيتم تشغيل الـ سيحدث Deadlock, سترمي الإستثناء RuntimeError.
الجدول التالي يحتوي على دوال الكلاس
الأكثر إستخداماً و التي سبق أن استخدمناها في الأمثلة السابقة.إسم الدالة مع تعريفها | |
---|---|
1 | نفعل لها Override لنضع فيها الأوامر التي نريدها أن تتنفذ عند تشغيل كائن الـ . |
2 | تستخدم لتشغيل كائن الـ الذي قام باستدعائها. فعلياً, الدالة تقوم فقط باستدعاء الدالة لتنفيذ الأوامر الموضوعة فيها. إنتبه: لا يمكن تشغيل نفس كائن الـ أكثر من مرة. أي لا يمكن إستدعاء الدالة أكثر من مرة من نفس كائن الـ . في حال تم استدعاء الدالة مرتين من نفس كائن الـ فإنها ترمي الإستثناء RuntimeError. |
3 | ترجع طالما أن كائن الـ لم ينتهي تنفيذه بعد حتى لو كان لم يتم البدء بتنفيذه أصلاً. غير ذلك ترجع . |
4 | تجعل كائن الـ الذي قام باستدعائها ينتظر إلى أن ينتهي تنفيذ الـ الذي يعمل قبله قبل أن يبدأ هو بتنفيذ الأوامر الموجودة فيه. في حال أردت تأخير تنفيذ الـ الذي سيعمل عند إنتهاء تنفيذ الـ لمدة محددة فيمكنك تمرير رقم مكان الباراميتر يمثل هذه المدة, مع الإشارة إلى أن الرقم الذي تمرره يمثل مدة الإنتظار بالثواني. فمثلاً إذا قمت بتمرير الرقم 1.5 فهذا يعني أنك تقصد ثانية و نصف. معلومة تقنية هذه الدالة مهمة عند الحاجة لجعل الـ Threads يتنفذوا الواحد تلو الآخر بدل أن يتنفذوا في وقت واحد و يسببوا مشكلة يقال لها Deadlock. و لا تقلق إذا لم تكن تعرف المقصود من مصطلح Deadlock الآن لأنك ستفهمها لاحقاً من الأمثلة. في حال وجد مترجم لغة بايثون أن عندما سيتم تشغيل الـ سيحدث Deadlock, سترمي الإستثناء RuntimeError. |
الجدول التالي يحتوي على خصائص الكلاس
.إسم الخاصية مع تعريفها | |
---|---|
1 | ترجع الإسم الذي تم إعطاؤه لكائن الـ الذي قام باستدعائها. فعلياً, عند إنشاء كائن من الكلاس الذي يرث من الكلاس يمكنك تمرير الإسم الذي تريد إعطاؤه له لحظة إنشاءه. |
2 | ترجع رقم التعرفة ( ID ) الذي يتم توليده بشكل عشوائي و إعطائه لكائن الـ الذي قام باستدعائها. |
3 | هذه الخاصية يمكن الإستفادة منها في حال كنت تنوي تشغيل Thread من داخل Thread آخر. إذا مررت لها القيمة قبل أن تقوم بتشغيل الـ Thread الآخر, سيفهم مترجم لغة بايثون أنك تنوي إيقاف الـ Thread الآخر بشكل تلقائي عندما يتوقف الـ Thread الذي قام باستدعائه في الأساس. كما أنه في حال كان الـ Thread الآخر متصل بملف, بقاعدة بيانات, أو بالإنترنت إلخ.. فإن إغلاقه بشكل مفاجئ لا يضمن أن يتم إغلاق الإتصالات التي كان يجريها. في حال وجد مترجم لغة بايثون أن عندما سيتم تشغيل الـ سيحدث Deadlock, سترمي الإستثناء RuntimeError. |
المزامنة
في حال كنت تريد تشغيل أكثر من Thread في نفس الوقت, يجب أن تنتبه جيداً إلى العمليات التي سيجريها كل Thread تنوي تشغيله لأن هذا الأمر قد يسبب لك مشاكل منطقية أو يعطيك نتائج خاطئة كما سنوضح لك في السيناريوهات التالية.
في حال كنت تريد تشغيل أكثر من Thread في نفس الوقت, يجب أن تنتبه جيداً إلى العمليات التي سيجريها كل Thread تنوي تشغيله لأن هذا الأمر قد يسبب لك مشاكل منطقية أو يعطيك نتائج خاطئة كما سنوضح لك في السيناريوهات التالية.
السيناريو الأول
في حال قمت ببناء Thread مهمته جلب علامات الطالب المخزنة في قاعدة بيانات و من ثم حساب معدله العام, و بعدما تم حساب المعدل و عرض المعدل للطالب, قام Thread آخر بتعديل بعض العلامات في قاعدة البيانات لأنه وجد أن الطالب عنده غياب كثير.
إذاً, النتيجة التي أعطانا إياها الـ Thread الأول في هذه الحالة ليست صحيحة, حيث أنه كان يفترض حساب المعدل بعد أن تم إدخال أيام الغياب ضمن المعادلة التي تحسب له معدله النهائي و تعرض له إن كان ناجحاً بناءاً على معدله النهائي و عدد الأيام التي حضر فيها إلى الجامعة.
فعلى سبيل المثال, قد يكون من شروط الجامعة أنه في حال تغيّب الطالب مدة 30 يوم خلال الفصل الواحد, يعتبر راسباً في كل المواد.
إذاً, لحل المشكلة السابقة, كان يجب مزامنة عمل الـ Thread الأول و الـ Thread الثاني.
أي كان يجب تشغيل الـ Thread الذي يجلب أيام الغياب أولاً.
ثم بعد تخزين أيام الغياب و توقف الـ Thread عن العمل, يجب تشغيل الـ Thread الذي يجلب علامات الطالب و يعطيه النتيجة النهائية.
في حال قمت ببناء Thread مهمته جلب علامات الطالب المخزنة في قاعدة بيانات و من ثم حساب معدله العام, و بعدما تم حساب المعدل و عرض المعدل للطالب, قام Thread آخر بتعديل بعض العلامات في قاعدة البيانات لأنه وجد أن الطالب عنده غياب كثير.
إذاً, النتيجة التي أعطانا إياها الـ Thread الأول في هذه الحالة ليست صحيحة, حيث أنه كان يفترض حساب المعدل بعد أن تم إدخال أيام الغياب ضمن المعادلة التي تحسب له معدله النهائي و تعرض له إن كان ناجحاً بناءاً على معدله النهائي و عدد الأيام التي حضر فيها إلى الجامعة.
فعلى سبيل المثال, قد يكون من شروط الجامعة أنه في حال تغيّب الطالب مدة 30 يوم خلال الفصل الواحد, يعتبر راسباً في كل المواد.
إذاً, لحل المشكلة السابقة, كان يجب مزامنة عمل الـ Thread الأول و الـ Thread الثاني.
أي كان يجب تشغيل الـ Thread الذي يجلب أيام الغياب أولاً.
ثم بعد تخزين أيام الغياب و توقف الـ Thread عن العمل, يجب تشغيل الـ Thread الذي يجلب علامات الطالب و يعطيه النتيجة النهائية.
السيناريو الثاني
في حال كان يوجد Thread يريد تعديل محتوى ملف, و كان يوجد Thread آخر يقوم بقراءة محتوى نفس الملف.
في هذه الحالة, سيحدث أيضاً خطأ و هو أن الـ Thread الذي يقوم بالقراءة, سيقرأ محتوى الملف القديم, بدون معرفة أنه قد تم تحديث محتوى هذا الملف في الوقت الذي كان يقرأ منه و يجري عمليات ما بناءاً على المحتوى الذي قرأه وقتها.
لحل هذه المشكلة, كان يجب مزامنة عمل الـ Thread الذي يقرأ من الملف و الـ Thread الذي يعدل في الملف لضمان أن لا يتعاملا معه في وقت واحد.
في حال كان يوجد Thread يريد تعديل محتوى ملف, و كان يوجد Thread آخر يقوم بقراءة محتوى نفس الملف.
في هذه الحالة, سيحدث أيضاً خطأ و هو أن الـ Thread الذي يقوم بالقراءة, سيقرأ محتوى الملف القديم, بدون معرفة أنه قد تم تحديث محتوى هذا الملف في الوقت الذي كان يقرأ منه و يجري عمليات ما بناءاً على المحتوى الذي قرأه وقتها.
لحل هذه المشكلة, كان يجب مزامنة عمل الـ Thread الذي يقرأ من الملف و الـ Thread الذي يعدل في الملف لضمان أن لا يتعاملا معه في وقت واحد.
السيناريو الثالث
في حال قمت بتشغيل إثنين Threads, و في مرحلة ما أصبح الإثنين عالقين بسبب أن الـ Thread الأول بحاجة للوصول إلى شيء يستخدمه الـ Thread الثاني. و بنفس الوقت الـ Thread الثاني بحاجة للوصول إلى شيء يستخدمه الـ Thread الأول. هذه المعضلة تسمى Deadlock, و يمكنك تخليها كما في الصورة التالية.
لحل هذه المشكلة, كان يمكن تشغيل كل Thread على حدا.
في المثال التالي قمنا بتعديل المثال السابق لجعل الـ Threads الذين ننشأهم من الكلاس
يعملون بطريقة متزامنة, أي الواحد تلو الآخر و ليس مع بعض.
ما فعلناه ببساطة, هو إنشاء كائن من الكلاس ثم وضع الكود الذي نريده أن يتنفذ بشكل متزامن بداخل بلوك من هذا الكائن. ,
ملاحظة: قمنا بتعليم الأسطر التي قمنا بإضافتها على الكود السابق باللون الأصفر.
في حال قمت بتشغيل إثنين Threads, و في مرحلة ما أصبح الإثنين عالقين بسبب أن الـ Thread الأول بحاجة للوصول إلى شيء يستخدمه الـ Thread الثاني. و بنفس الوقت الـ Thread الثاني بحاجة للوصول إلى شيء يستخدمه الـ Thread الأول. هذه المعضلة تسمى Deadlock, و يمكنك تخليها كما في الصورة التالية.
لحل هذه المشكلة, كان يمكن تشغيل كل Thread على حدا.
في المثال التالي قمنا بتعديل المثال السابق لجعل الـ Threads الذين ننشأهم من الكلاس
يعملون بطريقة متزامنة, أي الواحد تلو الآخر و ليس مع بعض.ما فعلناه ببساطة, هو إنشاء كائن من الكلاس ثم وضع الكود الذي نريده أن يتنفذ بشكل متزامن بداخل بلوك من هذا الكائن. ,
ملاحظة: قمنا بتعليم الأسطر التي قمنا بإضافتها على الكود السابق باللون الأصفر.
مثال
سنحصل على النتيجة التالية عند تشغيل الملف
.
في حال أردت تطبيق أسلوب المزامنة بدون إستخدام أسلوب
, يجب وضع الكود الذي تريد مزامنته بين الدالتين و .
إذاً يمكنك كتابة كود الدالة كالتالي و الحصول على نفس النتيجة.
سنحصل على النتيجة التالية عند تشغيل الملف
.في حال أردت تطبيق أسلوب المزامنة بدون إستخدام أسلوب
, يجب وضع الكود الذي تريد مزامنته بين الدالتين و .إذاً يمكنك كتابة كود الدالة كالتالي و الحصول على نفس النتيجة.
تجميع الـ Threads بداخل
في حال أردت تشغيل مجموعة Threads بشكل متزامن, يمكنك إنشاء كائن من الكلاس
و وضعهم فيه.
بعدها تقوم بإنشاء حلقة للمرور على Thread واحد منهم في كل مرة, و من ثم تقوم بتشغيله.
الجدول التالي يحتوي على دوال الكلاس
الأكثر إستخداماً.إسم الدالة مع تعريفها 1 ترجع العنصر التالي من كائن الـ الذي قام باستدعائها و من ثم تقوم بحذفه منها.
في حالتنا سترجع Thread جديد في كل مرة نستدعيها فيها.
في حال كان كائن الـ و تم استدعاءها فإنها ترمي الإستثناء Empty. 2 تضيف الكائن الذي نمرره لها كعنصر في كائن الـ الذي قام باستدعائها.
في حالتنا نمرر لها كائن من كلاس يمثل Thread. 3 ترجع عدد العناصر الموجودة في كائن الـ الذي قام باستدعائها.
في حالتنا عدد الـ Threads الموجودين فيه و الذين لم يتم تنفيذهم بعد. 4 تستخدم لمعرفة ما إن كان كائن الـ الذي قام باستدعائها فارغاً أم لا.
ترجع إذا كان فارغاً, غير ذلك ترجع . 5 تستخدم لمعرفة ما إن كان كائن الـ الذي قام باستدعائها ممتلىء أم لا, أي قادر على تخزين عناصر جديدة أم لا.
ترجع إذا كان عدد العناصر الموضوعة فيه أقل من عدد العناصر التي يمكنه تخزينها, غير ذلك ترجع .
في المثال التالي قمنا قمنا بإنشاء ثلاث كائنات تمثل Threads و وضعناهم بشكل مؤقت في list إسمه listWorkers. بعدها قمنا بتخزين كائنات الـ listWorkers في Queue إسمه
.
في الآخير قمنا بتشغيل كل الـ Threads الموضوعين في الكائن .ملاحظة: جعلنا الـ Threads يعملوا بشكل متزامن.
و بالتالي في حال لم ترد جعلهم يعملوا بشكل متزامن, سيكون عليك فقط إزالة جملة
الموضوعة في الدالة .
في حال أردت تشغيل مجموعة Threads بشكل متزامن, يمكنك إنشاء كائن من الكلاس
و وضعهم فيه.بعدها تقوم بإنشاء حلقة للمرور على Thread واحد منهم في كل مرة, و من ثم تقوم بتشغيله.
الجدول التالي يحتوي على دوال الكلاس
الأكثر إستخداماً.إسم الدالة مع تعريفها | |
---|---|
1 | ترجع العنصر التالي من كائن الـ الذي قام باستدعائها و من ثم تقوم بحذفه منها. في حالتنا سترجع Thread جديد في كل مرة نستدعيها فيها. في حال كان كائن الـ و تم استدعاءها فإنها ترمي الإستثناء Empty. |
2 | تضيف الكائن الذي نمرره لها كعنصر في كائن الـ الذي قام باستدعائها. في حالتنا نمرر لها كائن من كلاس يمثل Thread. |
3 | ترجع عدد العناصر الموجودة في كائن الـ الذي قام باستدعائها. في حالتنا عدد الـ Threads الموجودين فيه و الذين لم يتم تنفيذهم بعد. |
4 | تستخدم لمعرفة ما إن كان كائن الـ الذي قام باستدعائها فارغاً أم لا. ترجع إذا كان فارغاً, غير ذلك ترجع . |
5 | تستخدم لمعرفة ما إن كان كائن الـ الذي قام باستدعائها ممتلىء أم لا, أي قادر على تخزين عناصر جديدة أم لا. ترجع إذا كان عدد العناصر الموضوعة فيه أقل من عدد العناصر التي يمكنه تخزينها, غير ذلك ترجع . |
في المثال التالي قمنا قمنا بإنشاء ثلاث كائنات تمثل Threads و وضعناهم بشكل مؤقت في list إسمه listWorkers. بعدها قمنا بتخزين كائنات الـ listWorkers في Queue إسمه
.في الآخير قمنا بتشغيل كل الـ Threads الموضوعين في الكائن .
ملاحظة: جعلنا الـ Threads يعملوا بشكل متزامن.
و بالتالي في حال لم ترد جعلهم يعملوا بشكل متزامن, سيكون عليك فقط إزالة جملة
مثال
سنحصل على النتيجة التالية عند تشغيل الملف
.
سنحصل على النتيجة التالية عند تشغيل الملف
.الفرق بين الـ Process و الـ Thread بشكل عام
Process تعني برنامج شغال حالياً, و قد قام نظام التشغيل بحجز مساحة خاصة له في الذاكرة.
Thread عبارة عن مجموعة أوامر يتم تنفيذها أثناء تنفيذ أوامر أخرى في نفس البرنامج. يمكنك تشغيل أكثر من Thread في نفس الوقت في البرنامج, و يمكن أيضاً مشاركة المعلومات بينهم. مع ملاحظة أنه يتم إنشاء جميع الـ Threads من ضمن المساحة المحجوزة للـ Process في الذاكرة.
Process تعني برنامج شغال حالياً, و قد قام نظام التشغيل بحجز مساحة خاصة له في الذاكرة.
Thread عبارة عن مجموعة أوامر يتم تنفيذها أثناء تنفيذ أوامر أخرى في نفس البرنامج. يمكنك تشغيل أكثر من Thread في نفس الوقت في البرنامج, و يمكن أيضاً مشاركة المعلومات بينهم. مع ملاحظة أنه يتم إنشاء جميع الـ Threads من ضمن المساحة المحجوزة للـ Process في الذاكرة.
تعليقات
إرسال تعليق