CodeGym /مدونة جافا /Random-AR /الفرق بين Mutex، والشاشة، والإشارة
John Squirrels
مستوى
San Francisco

الفرق بين Mutex، والشاشة، والإشارة

نشرت في المجموعة
أهلاً! عندما درست تعدد العمليات على CodeGym، واجهت في كثير من الأحيان مفاهيم "mutex" و"monitor". دون إلقاء نظرة خاطفة، هل يمكنك أن تقول كيف تختلف؟ :) إذا كانت الإجابة بنعم، أحسنت! إذا لم يكن الأمر كذلك (وهذا هو الأكثر شيوعا)، فهذا ليس مفاجئا. "Mutex" و"الشاشة" هما في الواقع مفهومان مرتبطان. بالإضافة إلى ذلك، عندما تقرأ الدروس وتشاهد مقاطع فيديو حول تعدد العمليات على مواقع الويب الأخرى، ستصادف مفهومًا مشابهًا آخر: "الإشارة". كما أن لديها وظيفة مشابهة جدًا للشاشات وكائنات المزامنة. ولهذا السبب سنقوم بالتحقيق في هذه المصطلحات الثلاثة. سنلقي نظرة على بعض الأمثلة ونتوصل إلى فهم نهائي لكيفية اختلاف هذه المفاهيم عن بعضها البعض :)

موتيكس

كائن المزامنة (أو القفل) هو آلية خاصة لمزامنة سلاسل الرسائل. أحدهما "مرتبط" بكل كائن في Java - أنت تعرف ذلك بالفعل :) لا يهم إذا كنت تستخدم الفئات القياسية أو تقوم بإنشاء فئات خاصة بك، على سبيل المثال Cat and Dog : جميع الكائنات من جميع الفئات لها كائن المزامنة (mutex ) . مصطلح "mutex" يأتي من "الاستبعاد المتبادل"، الذي يصف الغرض منه بشكل مثالي. كما قلنا في أحد دروسنا السابقة، فإن كائن المزامنة يجعل من الممكن التأكد من أن مؤشر ترابط واحد فقط في كل مرة لديه حق الوصول إلى الكائن. من الأمثلة الواقعية الشائعة لكائن المزامنة (mutex) المراحيض. عندما يدخل الشخص إلى قسم المرحاض، فإنه يقوم بقفل الباب من الداخل. يشبه المرحاض كائنًا يمكن الوصول إليه عن طريق خيوط متعددة. يشبه القفل الموجود على باب التقسيم كائن المزامنة (mutex)، ويمثل صف الأشخاص بالخارج الخيوط. القفل الموجود على الباب هو كائن المرحاض: فهو يضمن دخول شخص واحد فقط إلى الداخل. ما الفرق بين كائن المزامنة (mutex) والشاشة والإشارة؟  - 2بمعنى آخر، يمكن لمؤشر ترابط واحد فقط في المرة الواحدة العمل مع الموارد المشتركة. ستفشل محاولات المواضيع الأخرى (الأشخاص) للوصول إلى الموارد المشغولة. يحتوي كائن المزامنة (mutex) على العديد من الميزات المهمة. أولاً ، هناك حالتان فقط ممكنتان: "مفتوح" و"مقفل". يساعدنا هذا على فهم كيفية عمله: يمكنك رسم أوجه التشابه مع المتغيرات المنطقية (صواب/خطأ) أو الأرقام الثنائية (0/1). ثانياً ، لا يمكن السيطرة على الدولة بشكل مباشر. لا تحتوي Java على آلية تتيح لك أخذ كائن بشكل صريح والحصول على كائن المزامنة (mutex) الخاص به وتعيين الحالة المطلوبة. بمعنى آخر، لا يمكنك فعل شيء مثل:
Object myObject = new Object();
Mutex mutex = myObject.getMutex();
mutex.free();
هذا يعني أنه لا يمكنك تحرير كائن المزامنة (mutex) للكائن. فقط جهاز Java لديه حق الوصول المباشر إليه. يعمل المبرمجون مع كائنات المزامنة من خلال أدوات اللغة.

شاشة

الشاشة عبارة عن "بنية فوقية" إضافية فوق كائن المزامنة (mutex). في الواقع، الشاشة عبارة عن جزء من التعليمات البرمجية "غير مرئية" للمبرمج. عندما تحدثنا عن كائنات المزامنة (mutexes) سابقًا، قدمنا ​​مثالًا بسيطًا:
public class Main {

   private Object obj = new Object();

   public void doSomething() {

       // ...some logic, available for all threads

       synchronized (obj) {

           // Logic available to just one thread at a time
       }
   }
}
في كتلة التعليمات البرمجية المميزة بالكلمة الأساسية المتزامنة ، يتم الحصول على كائن المزامنة (mutex) لكائن obj الخاص بنا. عظيم، يمكننا الحصول على القفل، ولكن كيف يتم توفير "الحماية" بالضبط؟ عندما نرى الكلمة متزامنة ما الذي يمنع الخيوط الأخرى من الدخول إلى الكتلة؟ الحماية تأتي من الشاشة! يقوم المترجم بتحويل الكلمة الأساسية المتزامنة إلى عدة أجزاء خاصة من التعليمات البرمجية. مرة أخرى، دعونا نعود إلى مثالنا مع طريقة doSomething() . وسنضيف إليها:
public class Main {

   private Object obj = new Object();

   public void doSomething() {

       // ...some logic, available for all threads

       // Logic available to just one thread at a time
       synchronized (obj) {

           /* Do important work that requires that the object
           be accessed by only one thread */
           obj.someImportantMethod();
       }
   }
}
إليك ما يحدث "تحت الغطاء" بعد أن يقوم المترجم بتحويل هذا الرمز:
public class Main {

   private Object obj = new Object();

   public void doSomething() throws InterruptedException {

       // ...some logic, available for all threads

       // Logic available to just one thread at a time:

       /* as long as the object's mutex is busy,
       all the other threads (except the one that acquired it) are put to sleep */
       while (obj.getMutex().isBusy()) {
           Thread.sleep(1);
       }

       // Mark the object's mutex as busy
       obj.getMutex().isBusy() = true;

       /* Do important work that requires that the object
       be accessed by only one thread */
       obj.someImportantMethod();

       // Free the object's mutex
       obj.getMutex().isBusy() = false;
   }
}
وبطبيعة الحال، هذا ليس مثالا حقيقيا. هنا، استخدمنا تعليمات برمجية تشبه Java لتصوير ما يحدث داخل جهاز Java. ومع ذلك، فإن هذا الكود الزائف يوفر فهمًا ممتازًا لما يحدث بالفعل مع الكائن والخيوط داخل الكتلة المتزامنة وكيف يقوم المترجم بتحويل هذه الكلمة الأساسية إلى عدة عبارات "غير مرئية" للمبرمج. في الأساس، تستخدم Java الكلمة الأساسية المتزامنة لتمثيل الشاشة . كل التعليمات البرمجية التي تظهر بدلاً من الكلمة الأساسية المتزامنة في المثال الأخير هي الشاشة.

إشارة

الكلمة الأخرى التي ستواجهها في دراستك الشخصية لتعدد مؤشرات الترابط هي "الإشارة". دعونا نتعرف على ما هو وكيف يختلف عن الشاشة وكائن المزامنة (mutex). الإشارة هي أداة لمزامنة الوصول إلى بعض الموارد. السمة المميزة لها هي أنها تستخدم عدادًا لإنشاء آلية المزامنة. يخبرنا العداد بعدد سلاسل العمليات التي يمكنها الوصول إلى المورد المشترك في نفس الوقت. ما الفرق بين كائن المزامنة (mutex) والشاشة والإشارة؟  - 3يتم تمثيل الإشارات في Java بواسطة فئة Semaphore . عند إنشاء كائنات الإشارة، يمكننا استخدام المنشئات التالية:
Semaphore(int permits)
Semaphore(int permits, boolean fair)
نمرر ما يلي إلى المنشئ:
    تصاريح int - القيمة الأولية والحد الأقصى للعداد. بمعنى آخر، تحدد هذه المعلمة عدد سلاسل العمليات التي يمكنها الوصول إلى المورد المشترك في نفس الوقت؛
  • معرض منطقي - يحدد الترتيب الذي سيتم من خلاله الوصول إلى سلاسل الرسائل. إذا كان عادلًا صحيحًا، فسيتم منح الوصول إلى سلاسل الرسائل المنتظرة بالترتيب الذي طلبته به. إذا كان خطأ، فسيتم تحديد الترتيب بواسطة برنامج جدولة الخيط.
أحد الأمثلة الكلاسيكية لاستخدام الإشارة هو مشكلة فيلسوف تناول الطعام. ما الفرق بين كائن المزامنة (mutex) والشاشة والإشارة؟  - 4ولتسهيل الفهم سنبسط الأمر قليلا. تخيل أن لدينا 5 فلاسفة يحتاجون إلى تناول الغداء. بالإضافة إلى ذلك، لدينا طاولة واحدة لا تتسع لأكثر من شخصين في نفس الوقت. مهمتنا هي إطعام جميع الفلاسفة. لا ينبغي لأحد منهم أن يجوع، ولا ينبغي لأي منهم أن "يمنع" بعضهم البعض عند محاولتهم الجلوس على الطاولة (يجب علينا تجنب الطريق المسدود). هذا ما سيبدو عليه صفنا الفيلسوف:
class Philosopher extends Thread {

   private Semaphore sem;

   // Did the philosopher eat?
   private boolean full = false;

   private String name;

   Philosopher(Semaphore sem, String name) {
       this.sem=sem;
       this.name=name;
   }

   public void run()
   {
       try
       {
           // If the philosopher has not eaten
           if (!full) {
               // Ask the semaphore for permission to run
               sem.acquire();
               System.out.println(name + " takes a seat at the table");

               // The philosopher eats
               sleep(300);
               full = true;

               System.out.println(name + " has eaten! He leaves the table");
               sem.release();

               // The philosopher leaves, making room for others
               sleep(300);
           }
       }
       catch(InterruptedException e) {
           System.out.println("Something went wrong!");
       }
   }
}
وإليك الكود لتشغيل برنامجنا:
public class Main {

   public static void main(String[] args) {

       Semaphore sem = new Semaphore(2);
       new Philosopher(sem, "Socrates").start();
       new Philosopher(sem,"Plato").start();
       new Philosopher(sem,"Aristotle").start();
       new Philosopher(sem, "Thales").start();
       new Philosopher(sem, "Pythagoras").start();
   }
}
لقد أنشأنا إشارة تم ضبط عدادها على 2 لتحقيق الشرط: يمكن لفيلسوفين فقط تناول الطعام في نفس الوقت. أي أنه يمكن تشغيل خيطين فقط في نفس الوقت، لأن فئة الفيلسوف لدينا ترث Thread ! تتحكم طريقتا الاكتساب () والإصدار () لفئة الإشارة في عداد الوصول الخاص بها. تطلب طريقة الاكتساب () من الإشارة الوصول إلى المورد. إذا كان العداد > 0، فسيتم منح الوصول وتقليل العداد بمقدار 1. "تحرر" طريقة الإصدار () الوصول الممنوح مسبقًا، وتعيده إلى العداد (يزيد عداد الوصول للإشارة بمقدار 1). ماذا نحصل عندما نقوم بتشغيل البرنامج؟ هل تم حل المشكلة؟ أفلا يقاتل فلاسفتنا وهم ينتظرون دورهم؟ :) إليك مخرجات وحدة التحكم التي حصلنا عليها:

Socrates takes a seat at the table 
Plato takes a seat at the table 
Socrates has eaten! He leaves the table
Plato has eaten! He leaves the table 
Aristotle takes a seat at the table 
Pythagoras takes a seat at the table 
Aristotle has eaten! He leaves the table
Pythagoras has eaten! He leaves the table 
Thales takes a seat at the table 
Thales has eaten! He leaves the table
لقد فعلناها! وعلى الرغم من أن طاليس كان عليه أن يتناول العشاء بمفرده، فلا أعتقد أننا أهنناه :) ربما لاحظت بعض أوجه التشابه بين كائن المزامنة (mutex) والإشارة المرورية. في الواقع، لديهم نفس المهمة: مزامنة الوصول إلى بعض الموارد. ما الفرق بين كائن المزامنة (mutex) والشاشة والإشارة؟  - 5والفرق الوحيد هو أنه يمكن الحصول على كائن المزامنة (mutex) الخاص بالكائن من خلال مؤشر ترابط واحد فقط في المرة الواحدة، بينما في حالة الإشارة، التي تستخدم عداد الخيوط، يمكن لعدة سلاسل رسائل الوصول إلى المورد في وقت واحد. هذه ليست مجرد صدفة :) كائن المزامنة (mutex) هو في الواقع إشارة ذات عدد 1. وبعبارة أخرى، إنها إشارة يمكنها استيعاب خيط واحد. تُعرف أيضًا باسم "الإشارة الثنائية" لأن عدادها يمكن أن يحتوي على قيمتين فقط - 1 ("غير مقفل") و0 ("مقفل"). هذا كل شيء! كما ترون، فإن الأمر ليس مربكًا للغاية بعد كل شيء :) الآن، إذا كنت ترغب في دراسة تعدد مؤشرات الترابط بمزيد من التفاصيل على الإنترنت، فسيكون من الأسهل عليك التنقل بين هذه المفاهيم. نراكم في الدروس القادمة!
تعليقات
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION