تم حل المشاكل عن طريق تعدد الخيوط
لقد تم اختراع تقنية Multithreading في الواقع لتحقيق هدفين مهمين:-
القيام بعدة أشياء في نفس الوقت.
في المثال أعلاه، قامت سلاسل مختلفة (أفراد الأسرة) بتنفيذ عدة إجراءات بالتوازي: غسل الأطباق، والذهاب إلى المتجر، وتعبئة الأشياء.
يمكننا أن نقدم مثالاً أكثر ارتباطًا بالبرمجة. لنفترض أن لديك برنامجًا مزودًا بواجهة مستخدم. عند النقر فوق "متابعة" في البرنامج، يجب أن تتم بعض الحسابات ويجب أن يرى المستخدم الشاشة التالية. إذا تم تنفيذ هذه الإجراءات بشكل تسلسلي، فسيتم تعليق البرنامج بعد أن يقوم المستخدم بالنقر فوق الزر "متابعة". سيظهر للمستخدم شاشة الزر "متابعة" حتى يقوم البرنامج بإجراء جميع الحسابات الداخلية ويصل إلى الجزء الذي يتم فيه تحديث واجهة المستخدم.
حسنًا، أعتقد أننا سننتظر بضع دقائق!
أو يمكننا إعادة صياغة برنامجنا، أو، كما يقول المبرمجون، "موازيته". لنجري حساباتنا على موضوع واحد ونرسم واجهة المستخدم على موضوع آخر. تمتلك معظم أجهزة الكمبيوتر موارد كافية للقيام بذلك. إذا سلكنا هذا الطريق، فلن يتجمد البرنامج وسيتحرك المستخدم بسلاسة بين الشاشات دون القلق بشأن ما يحدث بالداخل. ولا يتدخل أحدهما في الآخر :)
-
إجراء العمليات الحسابية بسرعة أكبر.
كل شيء أبسط بكثير هنا. إذا كان معالجنا يحتوي على العديد من النوى، ومعظم المعالجات اليوم تفعل ذلك، فيمكن للعديد من النوى التعامل مع قائمة المهام بالتوازي. من الواضح أنه إذا كنا بحاجة إلى تنفيذ 1000 مهمة وتستغرق كل منها ثانية واحدة، فيمكن لنواة واحدة إنهاء القائمة في 1000 ثانية، ونواتين في 500 ثانية، وثلاثة في ما يزيد قليلاً عن 333 ثانية، وما إلى ذلك.
public class MyFirstThread extends Thread {
@Override
public void run() {
System.out.println("I'm Thread! My name is " + getName());
}
}
لإنشاء سلاسل رسائل وتشغيلها، نحتاج إلى إنشاء فئة، وجعلها ترث java.lang . فئة مؤشر الترابط ، وتجاوز طريقة التشغيل () الخاصة بها. وهذا الشرط الأخير مهم جدا. في طريقة التشغيل () نحدد المنطق الذي سيتم تنفيذ مؤشر الترابط الخاص بنا فيه. الآن، إذا قمنا بإنشاء وتشغيل مثيل لـ MyFirstThread ، فستعرض طريقة run() سطرًا باسم: تعرض طريقة getName() اسم "النظام" الخاص بمؤشر الترابط، والذي يتم تعيينه تلقائيًا. ولكن لماذا نتحدث مبدئيا؟ دعونا ننشئ واحدًا ونكتشف ذلك!
public class Main {
public static void main(String[] args) {
for (int i = 0; i < 10; i++) {
MyFirstThread thread = new MyFirstThread();
thread.start();
}
}
}
إخراج وحدة التحكم: أنا خيط! اسمي الموضوع-2 أنا الموضوع! اسمي الموضوع-1 أنا الموضوع! اسمي الموضوع-0 أنا الموضوع! اسمي الموضوع-3 أنا الموضوع! اسمي الموضوع-6 أنا الموضوع! اسمي الموضوع-7 أنا الموضوع! اسمي Thread-4 أنا خيط! اسمي الموضوع-5 أنا الموضوع! اسمي الموضوع-9 أنا الموضوع! اسمي Thread-8 لننشئ 10 سلاسل ( كائنات MyFirstThread ، التي ترث Thread ) ونبدأها عن طريق استدعاء طريقة start () على كل كائن. بعد استدعاء أسلوب start() ، يتم تنفيذ المنطق في أسلوب run() . ملحوظة: أسماء المواضيع غير مرتبة. من الغريب أنهم لم يكونوا بالتسلسل: Thread-0 و Thread-1 و Thread-2 وما إلى ذلك؟ وكما حدث، فهذا مثال على الوقت الذي لا يناسب فيه التفكير "التسلسلي". تكمن المشكلة في أننا قدمنا فقط أوامر لإنشاء وتشغيل 10 سلاسل رسائل. يقرر برنامج جدولة الخيط، وهو آلية خاصة لنظام التشغيل، ترتيب التنفيذ. إن تصميمها الدقيق وإستراتيجية اتخاذ القرار هما موضوعان للمناقشة العميقة التي لن نتعمق فيها الآن. الشيء الرئيسي الذي يجب تذكره هو أن المبرمج لا يمكنه التحكم في ترتيب تنفيذ الخيوط. لفهم مدى خطورة الموقف، حاول تشغيل الطريقة main() في المثال أعلاه عدة مرات. إخراج وحدة التحكم في التشغيل الثاني: أنا خيط! اسمي الموضوع-0 أنا الموضوع! اسمي Thread-4 أنا خيط! اسمي الموضوع-3 أنا الموضوع! اسمي الموضوع-2 أنا الموضوع! اسمي الموضوع-1 أنا الموضوع! اسمي الموضوع-5 أنا الموضوع! اسمي الموضوع-6 أنا الموضوع! اسمي Thread-8 أنا خيط! اسمي الموضوع-9 أنا الموضوع! اسمي هو إخراج وحدة التحكم Thread-7 من التشغيل الثالث: أنا Thread! اسمي الموضوع-0 أنا الموضوع! اسمي الموضوع-3 أنا الموضوع! اسمي الموضوع-1 أنا الموضوع! اسمي الموضوع-2 أنا الموضوع! اسمي الموضوع-6 أنا الموضوع! اسمي Thread-4 أنا خيط! اسمي الموضوع-9 أنا الموضوع! اسمي الموضوع-5 أنا الموضوع! اسمي الموضوع-7 أنا الموضوع! اسمي الموضوع-8
المشاكل الناجمة عن تعدد العمليات
في مثالنا مع الكتب، رأيت أن تعدد مؤشرات الترابط يحل مهام مهمة جدًا ويمكن أن يجعل برامجنا أسرع. في كثير من الأحيان أسرع عدة مرات. لكن تعدد مؤشرات الترابط يعتبر موضوعًا صعبًا. وفي الواقع، إذا تم استخدامها بشكل غير صحيح، فإنها تخلق المشاكل بدلاً من حلها. عندما أقول "يخلق مشاكل"، لا أقصد بالمعنى المجرد. هناك مشكلتان محددتان يمكن أن يؤدي إليهما تعدد العمليات: حالة الجمود وظروف السباق. حالة الجمود هي حالة تنتظر فيها سلاسل رسائل متعددة الموارد التي تحتفظ بها بعضها البعض، ولا يمكن لأي منها الاستمرار في العمل. سنتحدث عنها أكثر في الدروس القادمة. يكفي المثال التالي في الوقت الحالي: تخيل أن Thread-1 يتفاعل مع بعض الكائنات-1، وأن Thread-2 يتفاعل مع الكائن-2. علاوة على ذلك، تم كتابة البرنامج بحيث:- يتوقف Thread-1 عن التفاعل مع Object-1 ويتحول إلى Object-2 بمجرد توقف Thread-2 عن التفاعل مع Object-2 ويتحول إلى Object-1.
- يتوقف Thread-2 عن التفاعل مع Object-2 ويتحول إلى Object-1 بمجرد توقف Thread-1 عن التفاعل مع Object-1 ويتحول إلى Object-2.
public class MyFirstThread extends Thread {
@Override
public void run() {
System.out.println("Thread executed: " + getName());
}
}
public class Main {
public static void main(String[] args) {
for (int i = 0; i < 10; i++) {
MyFirstThread thread = new MyFirstThread();
thread.start();
}
}
}
تخيل الآن أن البرنامج هو المسؤول عن تشغيل الروبوت الذي يقوم بطهي الطعام! الخيط 0 يخرج البيض من الثلاجة. يتم تشغيل الخيط 1 على الموقد. يحصل الخيط 2 على مقلاة ويضعها على الموقد. الخيط 3 يضيء الموقد. الخيط 4 يصب الزيت في المقلاة. الخيط 5 يكسر البيض ويصبه في المقلاة. يقوم الخيط 6 بإلقاء قشر البيض في سلة المهملات. يقوم الخيط 7 بإزالة البيض المطبوخ من الموقد. الخيط 8 يضع البيض المطبوخ على طبق. الخيط 9 يغسل الأطباق. انظر إلى نتائج برنامجنا: تم تنفيذ الموضوع: Thread-0 تم تنفيذ الموضوع: Thread-2 تم تنفيذ الموضوع Thread-1 تم تنفيذ الموضوع: Thread-4 تم تنفيذ الموضوع: Thread-9 تم تنفيذ الموضوع: Thread-5 تم تنفيذ الموضوع: Thread-8 Thread تم التنفيذ: الموضوع-7 تم تنفيذ الموضوع: الموضوع-3 هل هذا روتين كوميدي؟ :) وكل ذلك لأن عمل برنامجنا يعتمد على ترتيب تنفيذ المواضيع. مع أدنى انتهاك للتسلسل المطلوب، يتحول مطبخنا إلى جحيم، ويدمر الروبوت المجنون كل شيء من حوله. هذه أيضًا مشكلة شائعة في البرمجة متعددة الخيوط. سوف تسمع عنها أكثر من مرة. في ختام هذا الدرس، أود أن أوصي بكتاب حول تعدد العمليات. تمت كتابة "Java Concurrency in Practice" في عام 2006، لكنها لم تفقد أهميتها. إنه مخصص لبرمجة Java متعددة الخيوط - بدءًا من الأساسيات ووصولاً إلى الأخطاء والأنماط الأكثر شيوعًا. إذا قررت يومًا ما أن تصبح خبيرًا في تعدد مؤشرات الترابط، فهذا الكتاب يجب عليك قراءته. نراكم في الدروس القادمة! :)
GO TO FULL VERSION