تعدد المهام في بايثون
عندما تستخدم هاتفك أو حاسوبك, ترى أنه يمكنك تشغيل عدة برامج مع بعض في وقت واحد, كل برنامج شغال في الذاكرة يعتبر Process, فمثلاً إذا قمت بتشغيل خمسة برامج مع بعض فهذا يعني أن نظام التشغيل ينظم عمل خمسة Processes مع بعض. آلية تشغيل عدة برامج مع بعض تسمى Multiprocessing.
من جهة اخرى, في البرنامج الواحد يمكنك تنفيذ عدة أوامر مع بعض و جعل المستخدم يشعر كأنها تتنفذ في وقت واحد, فمثلاً في حال كنت تلعب لعبة مثل لعبة كرة القدم, تجد أنه هناك عدة أشياء تحدث في وقت واحد, فمثلاُ عند تشغيل اللعبة تسمع عدة أصوات ( مثل أغنية حماسية, صوت المعلق, صوت المشجعين, صوت صفارة الحكم في حال وقع خطأ إلخ.. ), بالإضافة إلى أنه يمكنك تحريك اللاعب و مشاهدة توقيت المبارة و الكثير من التفاصيل الأخرى التي تحدث كلها في نفس الوقت لتصنع لك لعبة رائعة. هذه الآلية تسمى Multithreading, لأن كل جزء شغال في البرنامج يكون عبارة عن مجموعة أوامر موضوعة بداخل Thread خاص.
إذاً نستخدم آلية Multithreading لجعل البرنامج قادر على تنفيذ عدة أوامر مع بعض و كأنها تتنفذ في وقت واحد, و هذا ما سنتعلمه في هذا الدرس تعدد المهام في بايثون .
أهمية تعدد المهام
تتلخص اهمية تعدد المهام في ما يلي :
جعل المستخدم قادر على تنفيذ عدة عمليات مع بعض في نفس الوقت.
جعل تصميم التطبيقات أجمل و إضافة مؤثرات فيه.
كل Thread تقوم بتشغيله, يعمل بشكل منعزل عن باقي الأوامر الموجودة في البرنامج, و بالتالي فإنه في حال وقوع أي خطأ في الـ Thread فإنه لن يؤثر على باقي الأوامر الموجود في البرنامج, كما أنه لا يؤثر على أي Thread آخر شغال في البرنامج.
طريقة إنشاء Thread في بايثون
في البداية, إبتداءاً من الإصدار 2.4 في بايثون, أصبح الموديول threading
هو الموديول الذي يستخدم لإنشاء Threads.
هذا الموديول عبارة عن موديول جاهز في بايثون, و هو يحتوي على الكلاس Thread
الذي يستخدم لبناء Thread و التحكم به.
إنتبه
قبل هذا الموديول كان يوجد موديول إسمه _thread
يستخدم لهذا الغرض أيضاً لكنه لم يعد يستخدم الآن و سيتم إلغاؤه مستقبلاً.
لذلك ننصح بعدم استخدام الموديول _thread
أو الإعتماد عليه.
خطوات بناء Thread في بايثون
يجب تضمين الموديول
threading
.بناء كلاس يرث من الكلاس
Thread
و تفعل Override لدالة إسمهاrun()
, و فيها تضع الأوامر التي تريدها أن تتنفذ عندما يتم تشغيل الـ Thread.
خطوات تشغيل Thread في بايثون
يجب إنشاء كائن من الكلاس الذي يرث من الكلاس
Thread
.من هذا الكائن, نقوم باستدعاء دالة جاهزة إسمها
start()
و التي ستقوم بشكل تلقائي باستدعاء الدالةrun()
و تنفيذ الأوامر الموضوعة فيها.
في المثال التالي قمنا بإنشاء كلاس إسمه Worker
يرث من الكلاس Thread
.
في الدالة __init__()
الخاصة بهذا الكلاس, قمنا بتعريف خاصية إسمها name
لأننا ننوي إعطاء إسم لكل كائن ننشئه من هذا الكلاس.
كما أننا جعلناها تستدعي الدالة __init__()
الموجودة في الكلاس Thread
لنكون قادرين على معاملة أي كائن ننشئه من هذا الكلاس كـ Thread.
بعدها فعلنا Override للدالة run()
بهدف أن تطبع الإسم الذي نضعه في الخاصية name
ثلاث مرات عندما يتم تشغيله.
في الأخير, قمنا بإنشاء كائنين من هذا الكلاس و تشغيلهما في وقت واحد.
مثال
import threading # Thread قمنا بتضمين هذا الموديول حتى نستطيع الوراثة من الكلاس import time # التي سنستخدمها في المثال sleep() قمنا بتضمين هذا الموديول لأنها تحتوي على الدالة # Thread يرث من الكلاس Worker هنا قمنا بإنشاء كلاس إسمه class Worker(threading.Thread): # Thread الموجودة في الكلاس __init__() من أجل إستدعاء الدالة __init__() هنا قمنا بتعريف الدالة # name أيضاً, من أجل تحديد أنه يجب تمرير إسم للكائن الذي يتم إنشاؤه من الكلاس و الذي سيتم تخزينه في الخاصية def __init__(self, name): super(Worker, self).__init__() self.name = name # لتحديد ما سيحدث عند تشغيل الكائن الذي ننشئه من هذا الكلاس run() هنا قمنا بتعريف الدالة def run(self): # في البداية سيتم طباعة إسم الكائن و أنه قد بدأ تنفيذه print('Starting', self.name) # sleep() بعدها سيتم طباعة إسم الكائن 3 مرات. و قمنا باستدعاء الدالة # و تمرير الرقم 1 لها لتجعل الحلقة تتوقف مدة ثانية في نهاية كل دورة for i in range(3): print(self.name) time.sleep(1) # في النهاية سيتم طباعة إسم الكائن و أنه قد إنتهى تنفيذه print('Ending', self.name)
# Worker الذي قمنا بإنشائه في الموديول Worker هنا قمنا بتضمين الكلاس from Worker import Worker # Threads أي قمنا بإنشاء إثنين .Worker هنا قمنا بإنشاء كائنين من الكلاس thread1 = Worker('Thread-1') thread2 = Worker('Thread-2') # مرة من كل كائن run() من كلا الكائنين لتشغيلهما. أي سيتم استدعاء الدالة start() هنا قمنا باستدعاء الدالة thread1.start() thread2.start() # لجعل مفسّر لغة بايثون thread2 و thread1 من الكائنين join() هنا وضعنا قمنا باستدعاء الدالة # قبل أن يتابع تنفيذ باقي الأوامر الموجودة في الملف thread2 و thread1 ينتظر أن يتوقف الكائنين thread1.join() thread2.join() # تماماً, سيتم تنفيذ أمر الطباعة التالي thread2 و thread1 بعد أن يتوقف الكائنين print('Both threads are end')
•سنحصل على نتيجة تشبه النتيجة التالية عند تشغيل الملف Test
.
Thread-1
Starting Thread-2
Thread-2
Thread-2
Thread-1
Thread-2
Thread-1
Ending Thread-2
Ending Thread-1
Both threads are end
ملاحظة
في المثال السابق, يمكنك حذف السطر 12 لأن الكلاس Worker
ورث أيضاً خاصية إسمها name
من الكلاس Thread
.
لهذا, يمكنك تمرير الإسم مباشرةً إلى الخاصية التي ورثها الكلاس Worker
بدل تعريفها فيه من جديد.
معلومة تقنية
عند تشغيل أكثر من Thread في وقت واحد, لا يمكنك ضمان أو تحديد أي Thread سيتنفذ أو ينتهي قبل الآخر.
السبب في ذلك أن معالج الحاسوب ( CPU ) سيقوم بإرسال كل Thread قمت بتشغيله إلى نواة ( Core ) حتى ينفذهم لك في وقت واحد.
و منطقياً, النواة التي عليها ضغط أقل ستنتهي من تنفيذ أوامر الـ Thread بشكل أسرع.
لهذا السبب, إذا قمت بتشغيل المثال السابق أكثر من مرة, ستجد أنك في كل مرة ستحصل على نتيجة مختلفة عن التشغيل.
مفهوم الـ Main Thread في بايثون
في بايثون, كل كود يتنفذ في البرنامج, فإنه حتماً يتنفذ بداخل Thread واحد على الأقل.
أي حتى لو لم تقم بوضع الكود بداخل Thread فإنه سيتم وضعه في Thread.
و بالتالي في حال قمت بتشغيل Thread في البرنامج, فهذا يعني أن البرنامج حالياً يعمل فيه إثنين Threads و ليس Thread واحد.
في المثال التالي, قمنا بطباعة عدد الـ Threads الذين يتنفذون حالياً علماً بأننا لم نقم بإنشاء أي كائن من كلاس يرث من الكلاس Thread.
مثال
import threading # Thread قمنا بتضمين هذا الموديول حتى نستطيع الوراثة من الكلاس # الذين يعملون حالياً Threads هنا قمنا بطباعة عدد الـ print('Active thread(s) count:', threading.active_count()) # تم تشغيله Thread الأساسي في البرنامج. أي أول Thread هنا قمنا بطباعة إسم الـ print('Main thread object:', threading.main_thread().name) # الحالي الذي يتم تنفيذه Thread هنا قمنا بطباعة إسم الـ print('Current thread object:', threading.main_thread().name)
•سنحصل على نتيجة تشبه النتيجة التالية عند تشغيل الملف Test
.
Main thread object: MainThread
Current thread object: MainThread
•نلاحظ أن عدد الـ Threads الذين يتنفذون حالياً هو 1, و هذا يثبت أن الكود الأساسي في البرنامج يتم وضعه في Thread.
•كما أن إسم الـ Thread الأساسي و إسم الـ Thread الذي يتنفذ حالياً هو MainThread
.
دوال الموديول threading
في بايثون
الجدول التالي يحتوي على دوال الموديول threading
الأكثر إستخداماً و التي سبق أن استخدمناها في الأمثلة السابقة.
إسم الدالة مع تعريفها | |
---|---|
threading.active_count()
ترجع عدد الـ Threads الذين يتنفذون في الوقت الحالي الذي تم فيه إستدعاءها. |
|
threading.main_thread()
ترجع كائن الـ thread الأساسي في البرنامج.معلومة: الـ thread الأساسي في البرنامج هو أول Thread بدأ مفسر لغة بايثون بتنفيذ الأوامر الموضوعة فيه. |
|
threading.current_thread()
ترجع كائن الـ thread الذي يتنفذ في الوقت الحالي الذي تم فيه إستدعاءها. |
|
threading.enumerate()
ترجع كائن list يحتوي على كل كائن thread يتنفذ في الوقت الحالي الذي تم فيه إستدعاءها.ملاحظة: لا ترجع أي thread تم إيقافه أو لم يتم تشغيله من الأساس. |
خصائص و دوال الكلاس Thread
في بايثون
الجدول التالي يحتوي على دوال الكلاس Thread
الأكثر إستخداماً و التي سبق أن استخدمناها في الأمثلة السابقة.
إسم الدالة مع تعريفها | |
---|---|
run()
نفعل لها Override لنضع فيها الأوامر التي نريدها أن تتنفذ عند تشغيل كائن الـ Thread . |
|
start()
تستخدم لتشغيل كائن الـ Thread الذي قام باستدعائها.فعلياً, الدالة start() تقوم فقط باستدعاء الدالة run() لتنفيذ الأوامر الموضوعة فيها.إنتبه: لا يمكن تشغيل نفس كائن الـ Thread أكثر من مرة. أي لا يمكن إستدعاء الدالة start() أكثر من مرة من نفس كائن الـ Thread .في حال تم استدعاء الدالة start() مرتين من نفس كائن الـ Thread فإنها ترمي الإستثناء RuntimeError. |
|
is_alive()
ترجع True طالما أن كائن الـ Thread لم ينتهي تنفيذه بعد حتى لو كان لم يتم البدء بتنفيذه أصلاً.غير ذلك ترجع False . |
|
join(timeout=None)
تجعل كائن الـ Thread الذي قام باستدعائها ينتظر إلى أن ينتهي تنفيذ الـ Thread الذي يعمل قبله قبل أن يبدأ هو بتنفيذ الأوامر الموجودة فيه.في حال أردت تأخير تنفيذ الـ Thread الذي سيعمل عند إنتهاء تنفيذ الـ Thread لمدة محددة فيمكنك تمرير رقم مكان الباراميتر timeout يمثل هذه المدة, مع الإشارة إلى أن الرقم الذي تمرره يمثل مدة الإنتظار بالثواني. فمثلاً إذا قمت بتمرير الرقم 1.5 فهذا يعني أنك تقصد ثانية و نصف.معلومة تقنية هذه الدالة مهمة عند الحاجة لجعل الـ Threads يتنفذوا الواحد تلو الآخر بدل أن يتنفذوا في وقت واحد و يسببوا مشكلة يقال لها Deadlock. و لا تقلق إذا لم تكن تعرف المقصود من مصطلح Deadlock الآن لأنك ستفهمها لاحقاً من الأمثلة. في حال وجد مترجم لغة بايثون أن عندما سيتم تشغيل الـ Thread سيحدث Deadlock, سترمي الإستثناء RuntimeError. |
الجدول التالي يحتوي على خصائص الكلاس Thread
.
إسم الخاصية مع تعريفها | |
---|---|
name
ترجع الإسم الذي تم إعطاؤه لكائن الـ Thread الذي قام باستدعائها.فعلياً, عند إنشاء كائن من الكلاس الذي يرث من الكلاس Thread يمكنك تمرير الإسم الذي تريد إعطاؤه له لحظة إنشاءه. |
|
ident
ترجع رقم التعرفة ( ID ) الذي يتم توليده بشكل عشوائي و إعطائه لكائن الـ Thread الذي قام باستدعائها. |
|
daemon
هذه الخاصية يمكن الإستفادة منها في حال كنت تنوي تشغيل Thread من داخل Thread آخر.إذا مررت لها القيمة True قبل أن تقوم بتشغيل الـ Thread الآخر, سيفهم مترجم لغة بايثون أنك تنوي إيقاف الـ Thread الآخر بشكل تلقائي عندما يتوقف الـ Thread الذي قام باستدعائه في الأساس. كما أنه في حال كان الـ Thread الآخر متصل بملف, بقاعدة بيانات, أو بالإنترنت إلخ.. فإن إغلاقه بشكل مفاجئ لا يضمن أن يتم إغلاق الإتصالات التي كان يجريها.في حال وجد مترجم لغة بايثون أن عندما سيتم تشغيل الـ Thread سيحدث Deadlock, سترمي الإستثناء RuntimeError. |
المزامنة في بايثون
في حال كنت تريد تشغيل أكثر من Thread في نفس الوقت, يجب أن تنتبه جيداً إلى العمليات التي سيجريها كل Thread تنوي تشغيله لأن هذا الأمر قد يسبب لك مشاكل منطقية أو يعطيك نتائج خاطئة كما سنوضح لك في السيناريوهات التالية.
السيناريو الأول
في حال قمت ببناء Thread مهمته جلب علامات الطالب المخزنة في قاعدة بيانات و من ثم حساب معدله العام, و بعدما تم حساب المعدل و عرض المعدل للطالب, قام Thread آخر بتعديل بعض العلامات في قاعدة البيانات لأنه وجد أن الطالب عنده غياب كثير.
إذاً, النتيجة التي أعطانا إياها الـ Thread الأول في هذه الحالة ليست صحيحة, حيث أنه كان يفترض حساب المعدل بعد أن تم إدخال أيام الغياب ضمن المعادلة التي تحسب له معدله النهائي و تعرض له إن كان ناجحاً بناءاً على معدله النهائي و عدد الأيام التي حضر فيها إلى الجامعة.
فعلى سبيل المثال, قد يكون من شروط الجامعة أنه في حال تغيّب الطالب مدة 30 يوم خلال الفصل الواحد, يعتبر راسباً في كل المواد.
إذاً, لحل المشكلة السابقة, كان يجب مزامنة عمل الـ Thread الأول و الـ Thread الثاني.
أي كان يجب تشغيل الـ Thread الذي يجلب أيام الغياب أولاً.
ثم بعد تخزين أيام الغياب و توقف الـ Thread عن العمل, يجب تشغيل الـ Thread الذي يجلب علامات الطالب و يعطيه النتيجة النهائية.
السيناريو الثاني
في حال كان يوجد Thread يريد تعديل محتوى ملف, و كان يوجد Thread آخر يقوم بقراءة محتوى نفس الملف.
في هذه الحالة, سيحدث أيضاً خطأ و هو أن الـ Thread الذي يقوم بالقراءة, سيقرأ محتوى الملف القديم, بدون معرفة أنه قد تم تحديث محتوى هذا الملف في الوقت الذي كان يقرأ منه و يجري عمليات ما بناءاً على المحتوى الذي قرأه وقتها.
لحل هذه المشكلة, كان يجب مزامنة عمل الـ Thread الذي يقرأ من الملف و الـ Thread الذي يعدل في الملف لضمان أن لا يتعاملا معه في وقت واحد.
السيناريو الثالث
في حال قمت بتشغيل إثنين Threads, و في مرحلة ما أصبح الإثنين عالقين بسبب أن الـ Thread الأول بحاجة للوصول إلى شيء يستخدمه الـ Thread الثاني. و بنفس الوقت الـ Thread الثاني بحاجة للوصول إلى شيء يستخدمه الـ Thread الأول. هذه المعضلة تسمى Deadlock, و هي يمكنك تخليها كما في الصورة التالية.
لحل هذه المشكلة, كان يمكن تشغيل كل Thread على حدا.
في المثال التالي قمنا بتعديل المثال السابق لجعل الـ Threads الذين ننشأهم من الكلاس Worker
يعملون بطريقة متزامنة, أي الواحد تلو الآخر و ليس مع بعض.
ما فعلناه ببساطة, هو إنشاء كائن من الكلاس Lock
, ثم وضع الكود الذي نريده أن يتنفذ بشكل متزامن بداخل بلوك من هذا الكائن.
ملاحظة: قمنا بتعليم الأسطر التي قمنا بإضافتها على الكود السابق باللون الأصفر.
مثال
import threading # Thread قمنا بتضمين هذا الموديول حتى نستطيع الوراثة من الكلاس import time # التي سنستخدمها في المثال sleep() قمنا بتضمين هذا الموديول لأنها تحتوي على الدالة # لأننا سنستخدمه لجعل أي كائن ننشئه من الكلاس يعمل بطريقة متزامنة Lock هنا قمنا بإنشاء كائن من الكلاس lock = threading.Lock() # Thread يرث من الكلاس Worker هنا قمنا بإنشاء كلاس إسمه class Worker(threading.Thread): # Thread الموجودة في الكلاس __init__() من أجل إستدعاء الدالة __init__() هنا قمنا بتعريف الدالة # name أيضاً, من أجل تحديد أنه يجب تمرير إسم للكائن الذي يتم إنشاؤه من الكلاس و الذي سيتم تخزينه في الخاصية def __init__(self, name): super(Worker, self).__init__() self.name = name # لتحديد ما سيحدث عند تشغيل الكائن الذي ننشئه من هذا الكلاس run() هنا قمنا بتعريف الدالة def run(self): # هنا قمنا بجعل الكود يتنفذ بطريقة متزامنة with lock: # في البداية سيتم طباعة إسم الكائن و أنه قد بدأ تنفيذه print('Starting', self.name) # sleep() بعدها سيتم طباعة إسم الكائن 3 مرات. و قمنا باستدعاء الدالة # و تمرير الرقم 1 لها لتجعل الحلقة تتوقف مدة ثانية في نهاية كل دورة for i in range(3): print(self.name) time.sleep(1) # في النهاية سيتم طباعة إسم الكائن و أنه قد إنتهى تنفيذه print('Ending', self.name)
# Worker الذي قمنا بإنشائه في الموديول Worker هنا قمنا بتضمين الكلاس from Worker import Worker # Threads أي قمنا بإنشاء إثنين .Worker هنا قمنا بإنشاء كائنين من الكلاس thread1 = Worker('Thread-1') thread2 = Worker('Thread-2') # مرة من كل كائن run() من كلا الكائنين لتشغيلهما. أي سيتم استدعاء الدالة start() هنا قمنا باستدعاء الدالة thread1.start() thread2.start() # لجعل مفسّر لغة بايثون thread2 و thread1 من الكائنين join() هنا وضعنا قمنا باستدعاء الدالة # قبل أن يتابع تنفيذ باقي الأوامر الموجودة في الملف thread2 و thread1 ينتظر أن يتوقف الكائنين thread1.join() thread2.join() # تماماً, سيتم تنفيذ أمر الطباعة التالي thread2 و thread1 بعد أن يتوقف الكائنين print('Both threads are end')
•سنحصل على النتيجة التالية عند تشغيل الملف Test
.
Thread-1
Thread-1
Thread-1
Ending Thread-1 <-- الأول Thread هنا إنتهى تنفيذ أوامر الـ
Starting Thread-2 <-- الثاني Thread هنا تم البدء بتنفيذ الـ
Thread-2
Thread-2
Thread-2
Ending Thread-2 <-- الثاني Thread هنا إنتهى تنفيذ أوامر الـ
Both threads are end
في حال أردت تطبيق أسلوب المزامنة بدون إستخدام أسلوب with block:
, يجب وضع الكود الذي تريد مزامنته بين الدالتين acquire()
و release()
.
إذاً يمكنك كتابة كود الدالة run()
كالتالي و الحصول على نفس النتيجة.
def run(self): block.acquire() # هنا قمنا بجعل الكود الذي سيتم تنفيذه بعدها يعمل بطريقة متزامنة print('Starting', self.name) for i in range(3): print(self.name) time.sleep(1) print('Ending', self.name) block.release() # هنا قمنا بإيقاف التزامن
تجميع الـ Threads بداخل Queue
في بايثون
في حال أردت تشغيل مجموعة Threads بشكل متزامن, يمكنك إنشاء كائن من الكلاس Queue
و وضعهم فيه.
بعدها تقوم بإنشاء حلقة للمرور على Thread واحد منهم في كل مرة, و من ثم تقوم بتشغيله.
الجدول التالي يحتوي على دوال الكلاس Queue
الأكثر إستخداماً.
إسم الدالة مع تعريفها | |
---|---|
get()
ترجع العنصر التالي من كائن الـ Queue الذي قام باستدعائها و من ثم تقوم بحذفه منها.في حالتنا سترجع Thread جديد في كل مرة نستدعيها فيها. في حال كان كائن الـ Queue و تم استدعاءها فإنها ترمي الإستثناء Empty. |
|
put(item)
تضيف الكائن الذي نمرره لها كعنصر في كائن الـ Queue الذي قام باستدعائها.في حالتنا نمرر لها كائن من كلاس يمثل Thread. |
|
qsize()
ترجع عدد العناصر الموجودة في كائن الـ Queue الذي قام باستدعائها.في حالتنا عدد الـ Threads الموجودين فيه و الذين لم يتم تنفيذهم بعد. |
|
empty()
تستخدم لمعرفة ما إن كان كائن الـ Queue الذي قام باستدعائها فارغاً أم لا.ترجع True إذا كان فارغاً, غير ذلك ترجع False . |
|
Full()
تستخدم لمعرفة ما إن كان كائن الـ Queue الذي قام باستدعائها ممتلىء أم لا, أي قادر على تخزين عناصر جديدة أم لا.ترجع True إذا كان عدد العناصر الموضوعة فيه أقل من عدد العناصر التي يمكنه تخزينها, غير ذلك ترجع False . |
في المثال التالي قمنا قمنا بإنشاء ثلاث كائنات تمثل Threads و وضعناهم بشكل مؤقت في list إسمه listWorkers.
بعدها قمنا بتخزين كائنات الـ listWorkers في Queue إسمه queueWorkers
.
في الآخير قمنا بتشغيل كل الـ Threads الموضوعين في الكائن queueWorkers
.
ملاحظة: جعلنا الـ Threads يعملوا بشكل متزامن.
و بالتالي في حال لم ترد جعلهم يعملوا بشكل متزامن, سيكون عليك فقط إزالة جملة with lock:
الموضوعة في الدالة run()
.
مثال
import threading # Thread قمنا بتضمين هذا الموديول حتى نستطيع الوراثة من الكلاس import time # التي سنستخدمها في المثال sleep() قمنا بتضمين هذا الموديول لأنها تحتوي على الدالة # لأننا سنستخدمه لجعل أي كائن ننشئه من الكلاس يعمل بطريقة متزامنة Lock هنا قمنا بإنشاء كائن من الكلاس lock = threading.Lock() # Thread يرث من الكلاس Worker هنا قمنا بإنشاء كلاس إسمه class Worker(threading.Thread): # Thread الموجودة في الكلاس __init__() من أجل إستدعاء الدالة __init__() هنا قمنا بتعريف الدالة # name أيضاً, من أجل تحديد أنه يجب تمرير إسم للكائن الذي يتم إنشاؤه من الكلاس و الذي سيتم تخزينه في الخاصية def __init__(self, name): super(Worker, self).__init__() self.name = name # لتحديد ما سيحدث عند تشغيل الكائن الذي ننشئه من هذا الكلاس run() هنا قمنا بتعريف الدالة def run(self): # هنا قمنا بجعل الكود يتنفذ بطريقة متزامنة with lock: # في البداية سيتم طباعة إسم الكائن و أنه قد بدأ تنفيذه print('Starting', self.name) # sleep() بعدها سيتم طباعة إسم الكائن 3 مرات. و قمنا باستدعاء الدالة # و تمرير الرقم 1 لها لتجعل الحلقة تتوقف مدة ثانية في نهاية كل دورة for i in range(3): print(self.name) time.sleep(1) # في النهاية سيتم طباعة إسم الكائن و أنه قد إنتهى تنفيذه print('Ending', self.name)
from Worker import Worker # Worker الذي قمنا بإنشائه في الموديول Worker هنا قمنا بتضمين الكلاس from queue import Queue # queue الموجود في الموديول Queue هنا قمنا بتضمين الكلاس # Thread أي وضعنا فيه 3 كائنات تمثل .Worker وضعنا فيه 3 كائنات من الكلاس list هنا قمنا بإنشاء workerList = [ Worker('Thread-1'), Worker('Thread-2'), Worker('Thread-3') ] # لأننا نريد وضع نفس العناصر بداخله workerList عدد عناصره يساوي عدد عناصر الكائن Queue هنا قمنا بإنشاء كائن workerQueue = Queue(len(workerList)) # workerQueue و من ثم تضيفه كعنصر في الكائن workerList في كل دورة ترجع كائن من الكائنات الموجودة في for هنا قمنا بإنشاء حلقة # workerQueue في الكائن workerList في النهاية سيتم وضع كل الكائنات الموجودة في الكائن for thread in workerList: workerQueue.put(thread) # غير فارغ workerQueue لا تتوقف عن تكرار ما بداخلها طالما أن الكائن while هنا قمنا بإنشاء حلقة # و من ثم تنفيذه workerQueue من الكائن Worker في كل دورة من دورات الحلقة, سيتم إخراج كائن while not workerQueue.empty(): workerQueue.get().start() # workerList في كل دورة ترجع كائن من الكائنات الموجودة في for هنا قمنا بإنشاء حلقة # لكي يجعل مفسّر لغى بايثون لا ينفذ أي أوامر أخرى موجودة بعد الحلقة join() ترجعه سيستدعي الدالة Worker كل كائن for thread in workerList: thread.join() # تماماً, سيتم تنفيذ أمر الطباعة التالي thread3 و thread2 ,thread1 بعد أن تتوقف الكائنات print('All threads are end')
•سنحصل على النتيجة التالية عند تشغيل الملف Test
.
Thread-1
Thread-1
Thread-1
Ending Thread-1 <-- الأول Thread هنا إنتهى تنفيذ أوامر الـ
Starting Thread-2 <-- الثاني Thread هنا تم البدء بتنفيذ الـ
Thread-2
Thread-2
Thread-2
Ending Thread-2 <-- الثاني Thread هنا إنتهى تنفيذ أوامر الـ
Starting Thread-3 <-- الثالث Thread هنا تم البدء بتنفيذ الـ
Thread-3
Thread-3
Thread-3
Ending Thread-3 <-- الثالث Thread هنا إنتهى تنفيذ أوامر الـ
All threads are end
الفرق بين الـ Process و الـ Thread بشكل عام
Process تعني برنامج شغال حالياً, و قد قام نظام التشغيل بحجز مساحة خاصة له في الذاكرة.
Thread عبارة عن مجموعة أوامر يتم تنفيذها أثناء تنفيذ أوامر أخرى في نفس البرنامج. يمكنك تشغيل أكثر من Thread في نفس الوقت في البرنامج, و يمكن أيضاً مشاركة المعلومات بينهم. مع ملاحظة أنه يتم إنشاء جميع الـ Threads من ضمن المساحة المحجوزة للـ Process في الذاكرة.
خطوتك التالية بعد تعلم لغة بايثون
مقدمة إذاقمت بقراءة كل الدروس التي شرحناها في هذه الدورة و وصلت إلى هنا, فهذا يعني أنك أصبحت ماهراً في التعامل مع لغة بايثون.
الشيء الذي عليك البحث عنه الآن, هو كيف تبدأ باستخدام لغة بايثون في المشاريع البرمجية من أجل أن تستفيد منها مادياً.
ماذا أتعلم بعد أن تعلمت لغة بايثون
في البداية, هناك مجالات عديدة يجب أن تتخصص بأحدها و ليس فيها كلها لأن هذا مستحيل.
أي لا تفكر أبداً بأن تتخصص في عدة مجالات في وقت واحد. لأنك إن حاولت ذلك فـأكد أنك في مرحلة ما ستيأس و تمل من شدة الضغط الذي سببته لنفسك.
إذاً, بعد أن تتعلم لغة بايثون, يمكنك إعتمادها في أحد المجالات التالية:
تطوير المواقع (Web Development).
الذكاء الإصطناعي (Artifical Intelligence).
تطوير تطبيقات للحاسوب (Software Development).
مؤثرات الألعاب (Animations).
تجهيز سكريبتات لتسريع عملك (Scripting).
إنتبه, تخصصك في مجال واحد لا يعني أنك لا تستطيع تعلم غيره. الفكرة هنا هي أن تتخصص في مجال واحد في كل مرة.
فعلى سبيل المثال, إذا قررت البدء بتعلم تطوير تطبيقات للحاسوب. عندها يجب أن تقضي فترة في تعلم ذلك و من ثم يجب أن تحاول بناء عدة تطبيقات بنفسك حتى تجد نفسك مرتاحاُ في هذا المجال و لا تخاف من البدء بتطوير تطبيقات و الربح منها مادياً.
بعد أن تجد نفسك أصبحت محترفاً في مجال تطوير تطبيقات الحاسوب, عندها نحن ننصحك بالبدء بتعلم مجال آخر من أجل أن تزيد فرصك في العمل و الربح.
فمثلاً إذا قررت تعلم تطوير المواقع بعد ذلك, فأنت الآن أصبحت قادر على العمل في مجالين و ليس في مجال واحد.
تطوير المواقع باستخدام لغة بايثون
إذا أردت الدخول في مجال تطوير المواقع, فعليك أولاً معرفة أي سيكون دور لغة بايثون في ذلك.
تطوير المواقع ينقسم لقسمين أساسيين: Front End و Back End.
قسم الـ Front End
يقصد منه تصميم الموقع و هذا لا علاقة له بلغة بايثون. و بشكل عام تحتاج معرفة الأشياء الأساسية التالية لمعرفة كيف تصمم أي موقع بنفسك من الصفر:
لغة HTML: هذه اللغة تستخدم لتحديد الأشياء التي تريد عرضها في الموقع مثل العناوين, الصور, الفيديوهات الخ..
لغة CSS: هذه اللغة تستخدم لتحسين تصميم و طريقة ظهور الأشياء التي تريد عرضها في الموقع.
لغة Javascript: هذه اللغة تستخدم لإضافة مؤثرات جميلة في الموقع.
مكتبة JQuery: من أجل كتابة كود الـ Javascript بشكل أسهل و مختصر.
مكتبة Bootstrap: من أجل جعل التصميم يتوافق مع حجم شاشة أي جهاز يتم تصفح الموقع من خلاله و هي توفر عليك جهد كبير جداً في التصميم.
مكتبة FontAwesome: لإضافة أيقونات رائعة في الموقع بكل سهولة.
قسم الـ Back End
في هذا القسم تستخدم لغة بايثون من أجل جعل ربط تصميم الموقع بقاعدة البيانات و كتابة كل الأوامر التي علاقها بإدارة البيانات.
بالنسبة لقواعد البيانات
تستطيع إستخدام أي قاعدة بيانات تريد مثل MySQL, SQL Server, Oracle إلخ..
بالنسبة للغة بايثون
تستطيع إستخدام أي إطار ( Framework ) جاهز تريد مثل Django, Pyramid, Flask إلخ.. لعلك تتساءل الآن لم يجب استخدام إطار بدل استخدام لغة بايثون بشكل مباشر!
عند استخدام إطار فأنت تريح رأسك من أشياء كثيرة مثل: الكود آمن و ليس فيه ثغرات, ستجد دوال و كلاسات كثيرة جاهزة يمكنك استخدامها كما هي بدل الإضطرار إلى كتابتها من الصفر, أي سؤال يخطر في بالك ستجد أجوبة له كثيرة في جوجل إلخ..
إذا كنت حائراً و لا تعرف أي قاعدة بيانات أو إطار بايثون أنسب لك, فيمكنك استخدام قواعد بيانات MySQL و الإطار Django لأنهما الأكثر شهرة.
بعد تعلم كل ما سبق, تصبح قادر على بناء أي موقع يخطر في بالك و العمل كمطور مواقع إن شاء الله.
الآن, في حال أردت تطوير نفسك أكثر في مجال الويب, يمكنك تعلم بناء تطبيقات ويب بدل بناء مواقع ويب تقليدية.
تطبيقات الويب لها عدة مميزات و أهمها أنها تجعلك قادر على تحويل الموقع إلى تطبيق يعمل على الهاتف و هذا الأمر يمكنه أن يؤمن لك فرص كثيرة أخرى للربح.
لبناء تطبيق ويب, سيكون عليك بالإضافة لم تعرفه سابقاً أن تتعلم استخدام إطار آخر من جهة Front End فقط.
أشهر الأطر المستخدمة حالياً لبناء تطبيقات ويب هي: Angular, Vue و React.
إستخدام لغة بايثون في الذكاء الإصطناعي
في البداية, عليك معرفة أن أفضل مجال لإستخدام لغة بايثون و أكثر سبب لشهرتها هو شهرتها في هذا المجال.
فكرة الذكاء الإصطناعي بشكل عام هي بناء برنامج قادر على إتخاذ القرارات و كأنه إنسان عادي!
لا بد و أنك تتساءل الآن حول كيف يمكن جعل البرنامج قادر على التفكير من تلقاء ذاته و هذا سؤال منطقي.
الذكاء الإصطناعي لا يعني أبداً جعل البرنامج يفكر و يتصرف تماماً من تلقاء ذاته, بل جعله يعتمد الأسلوب الذي يعتمده الناس في التصرف و اتخاذ القرارات.
سنحاول شرح طريقة عمل الذكاء الإصطناعي من خلال المثال التالي:
لنفترض أنه يوجد مستشفى يقوم يومياً بإلتقاط عدة صور أشعة لصدور المرضى, و من ثم يأتي الطبيب و ينظر إلى هذه الصورة فيكتب تقرير مفصل عنها.
بعدها يتم حفظ كل صورة مع التقرير الخاص بها في قاعدة بيانات المستشفى.
بعد مرور 5 سنوات على سبيل المثال, سيصبح لدى المستشفى قاعدة بيانات فيها آلاف الصور مع التقارير الخاصة بها.
بعد أن أصبح المستشفى يملك كم هائل من المعلومات, أصبح بإمكان هذه المستشفى الإستفادة من الذكاء الإصطناعي و إدخاله في عملها.
الآن, أصبح بإمكان المبرمج أن يجعل أي صورة أشعة يتم إلتقاتها تقارن مع الصور الموجودة في قاعدة البيانات و على أساس أكثر صورة أو صور مشابهة لها يتم طباعة التقرير للصورة الجديدة التي تم إلتقاطها.
فمثلاً بعد المقارنة, قد يجد البرنامج أنه يوجد إحتمال بنسبة 98% أن يكون هذا المريض يعاني من مشكلة معينة لأن أغلب الصور المشابهة لها تعاني من نفس المشكلة.
عندها و بشكل مباشر سيقوم البرنامج بإعطائك التقرير المفصل لهذه الصورة و كأنه قد قام شخص ما بإعداد هذا التقرير.
في النهاية, نتوصل أنه كلما تم تمديد البرنامج بمعلومات أكثر, كلما حصلنا على معلومات أدق.
و كخلاصة للموضوع كله, الذكاء الإصطناعي يبني قراراته على أساس المعلومات التي تموله بها أو التي قمت سابقاً بتجهزيها له.
مجال الذكاء الإصطناعي ينقسم بدوره لعدة فروع يمكنك تعلم إحداها مثل:
التعلم الآلي ( Machine Learning ) : يرتكز على جعل الآلة تحفظ المعلومات التي تسجلها من تلقاء نفسها ثم بناءاً على هذه المعلومات و الخوارزميات التي تعتمدها تعطيك النتيجة. فمثلاً, عند البحث في جوجل تجده بشكل تلقائي يظهر لك خيارات أثناء إدخال المعلومات التي تريد البحث عنها. هذه الخيارات تم تخزينها في الأصل بشكل تلقائي عندما قام أشخاص قبلك بالبحث كما تفعل أنت الآن.
الروبوتات الذكية ( Robotics ) : يرتكز على بناء آلات تجمع المعلومات من تلقاء ذاتها باعتماد خوارزميات معينة و من ثم تتصرف أو تؤدي المهمات التي تطلب منها على أساس المعلومات التي تعلمتها بنفسها. فمثلاً إذا طلبت من روبوت أن يصعد إلى الدرج, فإنه قد يقضي بعض الوقت في المرة الأولى و هو يجرب كيف يجب أن يتحرك حتى يصعد الدرج بنجاح. في النهاية و بعد أن يتوصل للطريقة الصحيحة التي يجب أن يتحرك بها لصعود الدرج فإنه عندما يريد صعوده في المرة المقبلة سيستخدم بسرعة نفس الأسلوب الذي تعلمه بنفسه سابقاً.
الرؤية ( Vision ) : يرتكز على جعل الآلة قادرة على معرفة طبيعة الأجسام التي تصورها و تحليلها, فمثلاً إذا قمت بإلتقاط صورة لقنينة ماء, فإنها قادرة على معرفة أن ما في الصورة عبارة عن قنينة بل و أن هذه القنينة ممتلئة على سبيل المثال بنسبة 80%. كمثال آخر, عندما تقوم بإضافة صورة على فيسبوك, فإن فيسبوك تجده بشكل تلقائي يضع علامة على الوجوه, بل و يخبرك أحياناً أن الشخص الذي في الصورة هو فلان و يسألك إن كنت تريد أن تفعل له منشين.
التخطيط ( Planning ) : يستخدم كثيراً في الألعاب حيث يتيح لك تحريك الشخصيات بشكل تلقائي. مثلاً لجعل الأشرار في اللعبة يقتربوا منك أو يهربوا منك إلخ.
معالجة الصوت ( Speech Processing ) : جعل الآلة قادرة على فهم الأصوات و تحويلها إلى كلام أو جعل لنطق الكلام المكتوب.
ملاحظة
ما قمنا بذكره عن مجالات الذكاء الإصطناعي يعتبر سطحي جداً لأن هذا المجال ضخم و مجالاته كثيرة.
ما هي الاشياء التي يجب معرفتها قبل الدخول في مجال الذكاء الإصطناعي
من الناحية البرمجية فيجب أن تعرف لغة بايثون و طريقة التعامل مع قواعد البيانات.
من ناحية أخرى فهذا المجال يتطلب مهارة كبيرة في الرياضيات إذا كان الشخص ينوي أن يبدع فيه.
من ناحية الرياضيات, يجب دراسة المواد التالية كبداية و التي يدرسها الطلاب في الجامعات:
Linear Algebra.
Calculus.
Probability and Statistics.
Discrete Mathematics.
Mathematical Logic.
في هذا الرابط تجد صور مبسطة جداً من شأنها أن توضح لك علاقة هذه المواد و أهميتها بالذكاء الإصطناعي يمكنك مشاهدته الموقع انقر هنا
تطوير تطبيقات للحاسوب باستخدام لغة بايثون
في بايثون يوجد أطر ( Frameworks ) عديدة يمكنك إستخدامها لبناء تطبيقات للحاسوب أو ما يعرف بتطبيقات سطح المكتب ( Desktop Apps ).
أشهر الأطر المستخدمة حالياً في بناء تطبيقات للحاسوب هي التالية:
Tkinter: يأتي مع ملفات بايثون نفسها.
Kivy: إطار مجاني يمكن استخدامه لبناء تطبيقات تعمل على نظام Windows, MacOSX, Android, IOS, Raspberry Pi.
PyQT: إطار يمكن استخدامه لبناء تطبيقات تعمل على نظام Windows, Unix/Linux و MacOSX. كما أنه يوجد منه نسخة مجانية و نسخة مدفوعة.
WxPython: إطار مجاني يمكن استخدامه لبناء تطبيقات شبيهة بالتطبيقات المنصبة على الجهاز ( Native Apps ) و تعمل على نظام Windows, MacOSX, Unix.
إستخدام لغة بايثون لإعداد مؤثرات للألعاب
في حال كنت من هواة تصميم الألعاب فيمكنك استخدام برنامج Blender المجاني لبناء الشخصيات و المؤثرات و حتى بناء مقاطع رسوم متحركة و ألعاب بسيطة.
اللغة البرمجية المستخدمة في هذا البرنامج من أجل إضافة المؤثرات هي بايثون.
برنامج Blender له شعبية كبيرة و مميزات قوية حقاً نسبة لأنه مجاني و يمكنك إيجاد شروحات كثيرة له في النت بالإضافة إلى المجسمات و الشخصيات و المؤثرات المجانية المعدة بواسطته.
إستخدام لغة بايثون لبناء سكريبتات تستخدم مفيدة
في حال كنت تقوم بمهام يومية أو شبه يومية في حاسوبك فيمكنك تجهيز سكريبت يفعل هذه المهام بمجرد أن تقوم بتشغيله.
فمثلاً, في حال كنت تقوم بشكل يومي بإجراء نسخة ثانية من ملفات موضوعة في مجلد ما, أن كنت تنشر نفس المقالات في عدة مجموعات في فيسبوك, أو تبحث في جوجل عن مواعيد الصلوات الخمسة إلخ.. عندها يمكنك إعداد الكود مرة واحدة, و عند الحاجة لتنفيذه تقوم بتشغيله فقط فيقوم بنفس المهام التي كنت ستقوم بها بنفسك و لن تضطر إلى الإنظزار و إضاعة وقتك في إجراء نفس العمليات في كل يوم.
كيف أطور نفسي الآن و أجد المعلومات التي أحتاجها
في البداية, كل ما تم ذكره في هذا الدرس يوجد مقدمات و دروس جاهزة حوله في يوتيوب و جوجل باللغتين العربية و الإنجليزية.
و بالتالي في أحال أردت معرفة المزيد حول أي شيء ذكرناه سابقاً قم بالبحث عنه فقط و تأكد أنك ستجد ملايين الإجابات الجاهزة و المفصلة.
روابط دورات عربية و أجنبية | |
---|---|
w3schools.com
اللغة: الإنجليزية يعتبر من أفضل المواقع على الإطلاق لتعلم اللغات الأساسية ( HTML, CSS, Javascript, JQuery, Bootstrap ) التي تستخدم في تصميم المواقع. كما أنه يحتوي على دورة تعلمك طريقة إستخدام الأيقونات المجانية التي توفرها لك المكتبة FontAwesome. |
|
Youtube: SQL Server
اللغة: الإنجليزية تعتبر من أفضل القنوات في يوتيوب لتعلم قواعد بيانات SQL Server من الصفر وصولاً لمستوى متقدم جداً فيها. من مميزات هذه الدورة, أن الأستاذ يتطرق أيضاً للأسئلة التي يتم طرحها في مقابلات العمل. |
|
Youtube: SQL Server
اللغة: العربية تعتبر من أفضل القنوات في يوتيوب لتعلم قواعد بيانات SQL Server من الصفر وصولاً لمستوى متقدم جداً فيها. من مميزات هذه الدورة, أن الأستاذ يتطرق أيضاً للأسئلة التي يتم طرحها في مقابلات العمل. و هي نفس الدورة السابقة و لكنها مدبلجة للغة العربية. |
|
Youtube: Flask
اللغة: الإنجليزية جميع قنوات يوتيوب التي قدمت دورات للإطار Flask. |
|
Youtube: Pyramid
اللغة: الإنجليزية جميع قنوات يوتيوب التي قدمت دورات للإطار Pyramid. |
|
Youtube: Tkinter
اللغة: الإنجليزية جميع قنوات يوتيوب التي قدمت دورات للإطار Tkinter. |
|
Youtube: Kivy
اللغة: الإنجليزية جميع قنوات يوتيوب التي قدمت دورات للإطار Kivy. |
|
Youtube: PyQT
اللغة: الإنجليزية جميع قنوات يوتيوب التي قدمت دورات للإطار PyQT. |
|
Youtube: WxPython
اللغة: الإنجليزية جميع قنوات يوتيوب التي قدمت دورات للإطار WxPython. |
|
Youtube: Blender
اللغة: الإنجليزية جميع قنوات يوتيوب التي قدمت دورات لبرنامج Blender. |
|
Youtube: Machine Learning
اللغة: الإنجليزية جميع قنوات يوتيوب التي قدمت دورات في مجال Machine Learning. |
|
Youtube: Robotics
اللغة: الإنجليزية جميع قنوات يوتيوب التي قدمت دورات في مجال Robotics. |
|
Youtube: Vision
اللغة: الإنجليزية جميع قنوات يوتيوب التي قدمت دورات في مجال Vision. |
|
Youtube: Speech Processing
اللغة: الإنجليزية جميع قنوات يوتيوب التي قدمت دورات في مجال Speech Processing. |
للأسف لم نستطع إيجاد روابط لدورات عربية قوية و مجانية لأن أغلب المطورين العرب للأسف لا يشاركون خبراتهم مجاناً في يوتيوب.
رسالة من الموقع تعلم البرمجة مجانا
نتمنى دعمنا من خلال مشاركة الدورة مع أصدقائك في مواقع التواصل الإجتماعي و زملائك في الجامعة إذا كنت تجد أن هذه الدروس يمكنها إفادته.
نتمنى لك كل التوفيق و نسأل الله أن يوفقك و يبارك لك في علمك و عملك.