اخطاء التزامن (الـ Concurrency) الشائعة

Mohammad Laifمنذ 3 سنوات

بسم الله الرحمن الرحيم
السلام عليكم ورحمة الله وبركاته

 

كما تعرفنا على التزامن وانواعه في الدرس السابق. في هذا الدرس سنتعرف على المشاكل والاخطاء الشائعة عند تطبيق التزامن Concurrency بشكل نظري. لذلك اذا رأيت او احسست بإخطاء في تطبيقك من هذا النوع فيجب عليك اعادة النظر في استخدامك للتزامن.
 

ملاحظة: هذه الدوره مختصه بالتزامن (concurrency) في نظام الاندرويد بلغة الجافا. اذا كنت تريد التعمق اكثر في موضوع التزامن فعليك بإلقاء نظره على التزامن في لغة الجافا.

 

تظهر المشاكل في الخيوط الحاسوبية عندما يكون لدينا اكثر من خيط حاسوبي واحد (سنتطرق لمفهوم الخيوط الحاسوبية في دروس اخرى لاحقاً) يقومون بمشاركة الحالة والموارد لمتغير ما في النظام! لفهم هذا الشئ دعنا نرى المثال الاتي.
 

مثال لبرنامج بنك

طلب منك تصميم برنامج لبنك. ومن كثرة العملاء ازداد حجم قاعدة البيانات وبدء البطئ يظهر في برنامجك. لذلك قمت بأستخدام التزامن Concurrency لإسراع قاعدة البيانات (كما سيأتي في درس لاحق) وانشئت ثلاثة خيوط حاسوبية للكتابة والقرائة من والى قاعدة البيانات, وهكذا قد تم حل المشكلة واصبح البرنامج سريع.

 

المشكلة
عميل قام بإيداع ٢٥ الف ريال وفجأه اصبح لديه ٧٥ الف ريال؟ من اين اتت الـ ٥٠ الف ريال الزائده؟

 

كيفية حدوث المشكلة

اولاً يجب الفهم بكيفية حدوث المشكلة. عندما قام العميل بإيداع الـ ٢٥, تم استلام الامر من قبل ثلاثة الخيوط معاً (لايوجد مزامنه Asynchronous بينهم). وبما ان هنالك مشكلة في التزامن بينهم كل خيط حاسوبي قام بإجراء العملية نفسها مما ادى الى وجود ٧٥ الف ريالاً.

 

الحل

تحتاج الى جعل هاؤلاء الخيوط يقومون بالمزامنة Synchronization, حتى لايكررون الامر او يغيرونه بدون علم الاخر.
 

من المثال السابق

اذن من المثال نستطيع الفهم ان مفهوم كلمة Concurrency جاء بمعنى ثاني وهو المنافسة. عندما تم التنافس بين الثلاثة خيوط لإداء شفرتك البرمجية.
وان كلمة Asynchronous جائت بمعنى غير متزامن, اي كل خيط ليس على وفاق مع الخيط الاخر.
وان كلمة Synchronization جائت بمعنى المزامنه, وهي جعل الخيوط على وفاق مع بعضها البعض.

 

من معاني الـ Concurrency: التزامن, التطابق, المزاحمة, المنافسة, المسابقة.

 

ولكن كيف تكونت هذه المشكلة؟

من الصعب جداً عمل Debug لهذا الامر ولكن نستطيع التخمين انها احدى هاتين المشكلتين:
 

سباق الشروط (الـ Race Conditions / Thread interference)
يحدث عندما يكون لدينا اكثر من خيط حاسوبي يقومون بالكتابة والقرائة في متغيرات او عنصر ما, مما يؤدي في بعض الاحيان الى التدخال او عدم التنسيق بينهم في القرائة والكتابة للعنصر. ونستطيع حل هذة المشكلة باستخدام تقنية القفل Lock. ولكنها تنتج مشاكل اخرى كالـ Deadlock و الـ Starvation.

 

تناسق الذاكرة (الـ Memory consistency)

يحدث عندما يكون لدينا اكثر من خيط حاسوبي يقومون بمشاركة متغير ما, ويقوم خيط بتحديث بيانات هذا المتغير بدون التصريح للخيوط الاخرى مما يؤدي الى عدم علمها بالتحديث الذي حصل.

 

الحلول

من حسن الحظ ان لغة الجافا بها العديد من الطرق لحل هذه المشاكل سنتعرف عليهم في النقاط التالية.
 

العناصر الذرية Atomic

في المثال السابق, ربما قمت بإستخدام عنصر boolean للتأكد هل تمت عملية الايداع ام لا. وبرغم انك قمت بجعله True عندما تتم العملية الإ ان المشكلة لم تحل. لماذا؟ ذلك بسبب ان النوع boolean ليس ذري Atomic!
تم انشاء العناصر الذرية لتقليل المشاكل المفتعله بين الخيوط الحاسوبية. فعندما يمتلك خيط حاسوبي عنصر ذري لايستطيع اي خيط اخر استخدامة الإعندما ينتهي الاول منه. فبدلاً من استخدام العنصر العادي boolean نستطيع استخدام AtomicBoolean. يوجد العديد من هذة العناصر تستطيع الاطلاع عليهم من هنا: java.util.concurrent.atomic.

 

الكلمات الخاصة بالتزامن (الـ Concurrency) في لغة الجافا

في لغة الجافا كلمات عديدة وبما انك مبرمج فبتأكيد صادفتك الكثير من هذة الكلمات مثل: If و Else و For و Private وماشبه ذلك. ايضاً هنالك كلمات خاصة بالتزامن Concurrency في لغة الجافا وهي Synchronized و Volatile سنتعرف عليهم في الفقرات التالية.
 

ماهي الـ Synchronization (متزامن)
عبارة عن الية لجعل الخيوط الحاسوبية (الـ Threads) في لغة الجافا تشارك الحالة والموارد لمتغير ما. وذلك من خلال صنع قفل Lock على المتغير المراد مشاركة مواردة وحالته, فقط خيط حاسوبي واحد يملك مفتاح هذا القفل في وقت واحد. ولايستطيع خيط حاسوبي اخر استخدام هذا المتغير الإ عندما يتم الخيط الاخر الانتهاء منه. 

 

كلمة synchronized في لغة الجافا

يتم استخدام كلمة synchronized في لغة الجافا لحل مشكلة التزامن (Synchronization) ربما صادفتها في بعض من الاكواد من قبل.
 

كلمة volatile في لغة الجافا
نقوم بإستخدامها مع كائن (فقط الحقول) لإعلام الـ Compiler بإن هذا الكائن متغير (متقلب بداخل الذاكرة). حتى يقوم الـ Compiler بالمراعاه في استخدامه. بمعنى اخر اذا كان لدينا كائن تتم معالجته بواسطة عدة خيوط حاسوبية نستخدم معه هذه الكلمة حتى لاتحصل اي مشكله معه.

 

تقنية القفل Lock

في الحقيقة ان كلمة الـ synchronized تقوم بإستخدام القفل Lock بشكل غير ظاهري (اي implicit). ويستخدم لصنع قفل على المتغير المراد مشاركة مواردة وحالته بين الخيوط.
 

مشكلة الـ Deadlock

تنتج هذه المشكلة عندما تقوم بمحاولة حل مشاكل التزامن, ولفهما لنفرض انك قمت بإقفال (Lock) متغير:

  • في الخيط الحاسوبي الاول وليكن حقل لإسم العميل.
  • وفي الخيط الحاسوبي الثاني قمت بإقفال رقم هاتف العميل.
  • الان الخيط الاول يحتاج الى رقم هاتف العميل وسينتظر الى ان ينتهي منه الخيط الثاني.
  • ايضاً في نفس الوقت الخيط الثاني يحتاج الى اسم العميل وسينتظر الى ان ينتهي منه الخيط الاول.

النتيجة: انتظار لانهائي, تعطل التطبيق.
 

راينا في هذا الدرس ان الجافا لديها العديد من الطرق والكلمات الخاصة لإنشاء مزامنة Concurrency خالية من المشاكل. والان بعد ان تعرفت على اغلب مشاكل المزامنة فعندما تواجهك مشكلة تعتقد انها بسبب عدم المزامنة الصحيحة تستطيع عمل Debug والبحث لإيجاد حلول لها. ومن افضل المراجع التي تستطيع الرجوع لها لحل مشاكلك وفهمها كتاب: Java Concurrency in Practice. ولكن لاتقلق جائت الـ Android SDK بالعديد من انماط التصميم الجاهزة لتبعد عنك حدوث هذه المشاكل قدر المستطاع ومنها النمط المتعارف عليه AsyncTask وانماط اخرى سنتعرف عليهم في الدروس القادمة.
 

المصادر والمراجع

للمزيد راجع درس المقدمة.

 

النهاية
لاتنسى تتبع الدورة لإشعارك عندما يتم التعديل على المتحوى او اضافة المزيد. ايضاً لاتنسى الاعجاب بها ومشاركتها مع الاخرين لتعم الفائده.

المحاضر

Mohammad Laif

محتوى الدورة

تمهيد
1 مقدمة
2 تعرف على التزامن (الـ Concurrency)
3 اخطاء التزامن (الـ Concurrency) الشائعة الدرس الحالي
العمليات Processes
1 الهيكلة الهندسية لبيئة نظام الاندرويد
2 طبقة الـ Android Runtime و العمليات Processes
3 انواع العمليات (Processes)
الخيوط الحاسوبية Threads
1 الخيوط الحاسوبية (Threads)
2 مكونات الخيوط الحاسوبية (Thread)
3 انشاء الخيط الحاسوبي
أنماط التصميم للخيوط الحاسوبية
1 انشاء نمط الـ Handler و Looper و Thread
2 انشاء نمط الـ HandlerThreads
3 ماهو نمط الـ Thread Pools (الـ Executors)
4 استخدام نمط الـ Thread Pools كـ Singleton
5 استخدام الـ Callable مع نمط الـ Thread Pools
6 انشاء بركة خيوط حاسوبية Thread Pools لتسريع قاعدة البيانات
7 انشاء نمط الـ AsyncTask
8 انشاء نمط الـ Loader
الـ Broadcasts
1 تعرف على الـ Broadcast Receiver
2 انشاء الـ Broadcast Receiver بشكل ثابت
3 انشاء الـ Broadcast Receiver بشكل ديناميكي
4 استخدام الـ Local Broadcast للتخاطب بين المكونات
الـ Services
1 تعرف على الـ Services
2 انشاء الـ Started Service
3 انشاء الـ Intent Service
4 انشاء الـ Bound Service
5 استخدام الـ ResultReceiver للتخاطب مع الـ Intent Service
6 استخدام الـ Broadcast للتخاطب مع الـ Started Service
الـ Alarm Manager
1 تعرف على الـ Alarm Manager
2 طرق استخدام الـ Alarm Manager
الـ Jobs
1 استخدام الـ Android JobSchedualer
2 استخدام الـ Firebase JobDispatcher
3 استخدام الـ WorkManager من حزمة JetPack

الكلمات الدليلية

عن الدرس

5 إعجاب
3 متابع
0 مشاركة
2226 مشاهدات
منذ 3 سنوات

التعليقات (0)

لايوجد لديك حساب في عالم البرمجة؟

تحب تنضم لعالم البرمجة؟ وتنشئ عالمك الخاص، تنشر المقالات، الدورات، تشارك المبرمجين وتساعد الآخرين، اشترك الآن بخطوات يسيرة !