تقييم الموضوع :
  • 0 أصوات - بمعدل 0
  • 1
  • 2
  • 3
  • 4
  • 5
تزامن المسارات Thread Synchronization
#2

The MethodImpl Attribute

في معظم الحالات مزامنة فئة كاملة ستقتل التطبيق وحماية بعض الطرائق في تلك الفئة يكون كافيا في معظم الحالات حيث يمكنك تطبيق هذا بتغليف كود الطريقة بكتلة SyncLock أو يمكنك استخدام تقنية أبسط مبنية على الصفة System.Runtime.CompilerServices.MethodImpl

كود :
Class MethodImplDemoClass
' This method can be executed by one thread at a time.
<MethodImpl(MethodImplOptions.Synchronized)> _
Sub SynchronizedMethod()

End Sub
End Class
فتطبيق الصفة MethodImpl على عدة طرائق في الفئة يؤدي نفس الغرض من تغليف كامل تلك الطرائق بكتلة SyncLock والتي تستخدم Me كمتغير إقفال وبكلمات أخرى أي مسار يستدعي طريقة معلمة بالخاصية MethodImpl سوف يمنع أي مسار آخر من استدعاء الطريقة المعلمة بالخاصية MethodImpl كما يمكنك استخدام هذه الخاصية على الطرائق الساكنة ويكون متغير الغرض الذي يستخدم ضمنيا لقفل الطرائق الساكنة مختلف عن متغير الغرض المستخدم لقفل الطرائق الأخرى للفئة instance methods وبهذا فالمسار الذي يستدعي طريقة ساكنة معلمة بالصفة MethodImpl لا يمنع مسار آخر من استدعاء الطرائق الغير ساكنة instance methods والمعلمة بنفس الصفة

عمليات القراءة والكتابة المتغيرة Volatile Read and Write Operations
عندما تتم مشاركة متغير عبر عدة مسارات والتطبيق يعمل على حاسب متعدد المعالجات يجب عليك وضع احتمال حدوث أخطاء إضافية في الحسبان وتكمن المشكلة في النظام متعدد المعالجات في أن لكل معالج الكاش الخاص به ولهذا فإذا قمت بالكتابة على حقل في فئة على مسار سيتم كتابة القيمة الجديدة في الكاش المرتبط مع المعالج الحالي ولا يتم نشرها مباشرة إلى الكاش الخاص ببقية المعالجات بحيث يمكنهم جميعا رؤية القيمة الجديدة. كما تحدث مشكلة مشابهة في الأنظمة ذات المعالج 64 بت الذي يمكنه إعادة ترتيب تنفيذ كتل عبارات الكود متضمنا عمليات القراءة والكتابة في الذاكرة وعملية إعادة الترتيب لم يكن لها تأثير ظاهر حتى الآن من أجل مسار واحد يستخدم جزءا معينا من الذاكرة ولكن ربما سيسبب ذلك مشكلة عندما يتم الوصول إلى نفس الجزء من الذاكرة بواسطة عدة مسارات. وتوفر الفريموورك حلان لهذه المشكلة وهما وزج من الطرائق VolatileRead و VolatileWrite والطريقة MemoryBarrier ويوفرها جميعا النوع Thread
تمكنك الطريقة VolatileWrite من كتابة متغير والتأكد من أن القيمة الجديدة يتم كتابتها آليا في الذاكرة المشتركة بين جميع المعالجات ولا تبقى في المسجل الخاص بالمعالج حيث تكون مخفية عن بقية المسارات وبالمثل تمكنك الطريقة VolatileRead من قراءة المتغير بطريقة آمنة لأنها تجبر النظام على تفريغ جميع ذواكر الكاش الموجودة قبل تنفيذ العملية وكلا الطريقتان محملتان تحميلا زائدا Overloaded بحيث تأخذ متغيرات رقمية أو غرضية Object وبالمرجع كما في قطعة الكود التالية

كود :
Class TestClass
Private Shared sharedValue As Integer

Function IncrementValue() As Integer
Dim value As Integer = Thread.VolatileRead(sharedValue)
value += 1
Thread.VolatileWrite(sharedValue, value)
Return value
End Function
End Class
والطريقتان المذكورتان تعملان بشكل جيد عندما نتعامل مع المتغيرات الرقمية أو الغرضية Object ولكن لا يمكن استخدامهما من أجل أنواع أخرى من المتغيرات لأنه لا يمكنك استخدام نسخة الدالة التي تأخذ متغير من النوع Object بسبب عدم إمكانية الاعتماد على عملية التحويل عندما يكون المتغير ممررا بالمرجع مما يقودنا إلى الطريقة MemoryBarrier التي تقوم بتفريغ محتويات جميع ذواكر الكاش الخاصة بالمعالجات إلى الذاكرة الرئيسية وبهذا تضمن لك أن جميع المتغيرات تحتوي أحدث نسخة من البيانات التي تمت كتابتها إليهم فمثلا يضمن الكود التالي أن الفئة Singleton تعمل جيدا حتى على نظام متعدد المعالجات

كود :
Class Singleton
Private Shared m_Instance As Singleton
Private Shared sharedLock As New Object()

Public Shared ReadOnly Property Instance() As Singleton
Get
If m_Instance Is Nothing Then
SyncLock sharedLock
If m_Instance Is Nothing Then
Dim tempInstance As Singleton = New Singleton()
' Ensure that writes related to instantiation are flushed.
Thread.MemoryBarrier()
m_Instance = tempInstance
End If
End SyncLock
End If
Return m_Instance
End Get
End Property
End Class
ويجب عليك استدعاء الطريقة MemoryBarrier مباشرة قبل أن يتم نشر القيمة الجديدة إلى بقية المسارات وفي المثال السابق يتم التأكد من اكتمال وضع القيمة في المتغير tempInstance قبل أن توضع في المتغير الذي ستتم مشاركته عبر المسارات

The Monitor Type
توفر كتلة SyncLock طريقة سهلة لاستخدام طريقة تتعامل مع مسائل التزامن ولكنها تكون غير ملائمة في العديد من الحالات فمثلا لا يمكن للمسار اختبار كود في كتلة SyncLock وتجنب منعه من ذلك إذا كان مسار آخر ينفذ كتلة SyncLock مرتبطة مع نفس الغرض Object وكتل SyncLock معرفة داخليا بواسطة Monitor objects التي يمكن استخدامها مباشرة للحصول على مرونة أكثر ويتم ذلك على حساب زيادة التعقيد في الكود. ولا يمكنك استخدام Monitor object وحيد وفي الحقيقة جميع طرق Monitor type التي سيتم عرضها هي طرائق ساكنة وتعتبر Enter هي الطريقة الأهم وهي تأخذ بارامتر من النوع Object الذي يعمل كالبارامتر الممرر لكتلة SyncLock وتكون له نفس الشروط من كونه من نوع مرجعي ومشترك ولا يمكن أن يحمل القيمة Nothing وإن لم تمتلك المسارات الأخرى قفلا على هذا الغرض فيقوم المسار الحالي بطلب ذلك القفل ويضبط قيمة العداد إلى 1 وإن امتلك مسار آخر القفل يجب على المسار الطالب انتظار أن يقوم المسار الآخر بتحرير القفل حتى يصبح متوفرا وإن كان المسار الطالب يمتلك القفل أساسا يؤدي كل استدعاء للطريقة Monitor.Enter إلى زيادة قيمة العداد. وتأخذ الطريقة Monitor.Exit غرض القفل lock object كبارامتر لها وتنقص قيمة العداد وعندما تصل قيمة العداد للصفر يتم تحرير القفل ممكنا بقية المسارات من الحصول عليه ويجب أن يتم الموازنة بين استدعاء الطريقة Monitor.Enter والطريقة Monitor.Exit أو لن يتم تحرير القفل أبدا

كود :
' A non-Nothing module-level object variable
Dim objLock As New Object()

Try
' Attempt to enter the protected section;
' wait if the lock is currently owned by another thread.
Monitor.Enter(objLock)
' Do something here.

Finally
' Release the lock.
Monitor.Exit(objLock)
End Try
إذا كان هناك احتمال في أن تطلق العبارات الموجودة بين الطريقتان Enter و Exit استثناء يجب عليك عندها وضع كامل الكود ضمن كتلة Try…End Try لأنه من الضروري أن تقوم بتحرير القفل دوما وإن طلب مسار طريقة على مسار آخر تنتظر داخل الطريقة Monitor.Enter سوف يستقبل ذلك المسار استثناء ThreadInterruptedException الذي يعتبر سببا إضافيا لاستخدام كتلة Try…End Try والطريقتان Enter و Exit الخاصتين بـ Monitor Object يسمحان لك باستبدال كتلة SyncLock ولكنهما لا يقدمان لك أية فوائد إضافية وسوف ترى المرونة الزائدة للفئة Monitor عندما تطبق الطريقة TryEnter وهي مشابهة للطريقة Enter ولكنها تخرج وتعيد False إذا كان لا يمكن الحصول على القفل خلال فترة زمنية محددة فمثلا يمكنك محاولة الحصول على Monitor خلال 10 ميللي ثانية ثم التخلي عن ذلك دون أن توقف المسار الحالي مدة غير محددة ويقوم الكود التالي بإعادة كتابة المثال السابق المعتمد على SyncLock مستخدما Monitor object ويظهر لك المحاولات الفاشلة للحصول على القفل

كود :
Try
Do Until Monitor.TryEnter(consoleLock, 10)
Debug.WriteLine("Thread " + Thread.CurrentThread.Name + _
" failed to acquire the lock")
Loop
' Split the output line in pieces.
Console.Write(" ")
Console.Write(Thread.CurrentThread.Name)
Finally
' Release the lock.
Monitor.Exit(consoleLock)
End Try
The Mutex Type
يوفر النوع Mutex مبدأ آخر للتزامن حيث أن الـ Mutex هو Windows kernel object يمكن امتلاكه من قبل مسار واحد فقط في الوقت نفسه ويكون في حالة إشارة a signaled state عندما لا يمتلكه أي مسار. ويطلب المسار ملكية الـ Mutex باستخدام الطريقة الساكنة Mutex.WaitOne والتي لا تعود إلا بعد أن يتم تحقيق الملكية ويتم تحريرها باستخدام الطريقة الساكنة Mutex.ReleaseMutex والمسار الذي يطلب ملكية Mutex object المملوك من قبله سلفا لا يمنع نفسه من الحصول على الملكية فيجب عليك في هذه الحالة استدعاء ReleaseMutex بعدد مساوي من المرات وهذا مثال عن كيفية تعريف قسم متزامن باستخدام Mutex object

كود :
' This Mutex object must be accessible to all threads.
Dim m As New Mutex()

Sub WaitOneExample()
m.WaitOne()
' Enter the synchronized section.

' Exit the synchronized section.
m.ReleaseMutex()
End Sub
وفي التطبيقات الحقيقية عليك استخدام كتلة Try لحماية كودك من الأخطاء ووضع استدعاء ReleaseMutex في قسم Finally وإن قمت بتمرير بارامتر اختياري للطريقة WaitOne كزمن انتهاء فستعيد التحكم للمسار عندما يتم تحقيق الملكية بنجاح أو عندما ينتهي الوقت المحدد ويمكن معرفة الفرق بين النتيجتين باختبار القيمة المعادة حيث أن True تعني تحقيق الملكية و False تعني انتهاء الوقت

كود :
' Attempt to enter the synchronized section, but give up after 0.1 seconds.
If m.WaitOne(100, False) Then

' Enter the synchronized section.

' Exit the synchronized section, and release the mutex.
m.ReleaseMutex()
End If
عند استخدام هذه الطريقة يوفر النوع Mutex آلية مكافئة للطريقة Monitor.TryEnter بدون تقديم أية خصائص إضافية ويمكنك رؤية المرونة الإضافية للنوع Mutex عندما ترى الطريقتين الساكنتين WaitAny و WaitAll الخاصتين به والطريقة WaitAny تأخذ مصفوفة من Mutex objects وتعود عندما تحقق ملكية واحد من Mutex objects من تلك القائمة وفي هذه الحالة يصبح الـ Mutex في حالة إشارة أو عندما ينتهي الوقت المحدد بالبارامتر الاختياري والقيمة المعادة تكون عبارة عن مصفوفة من Mutex objects التي أصبحت في حالة إشارة أو قيمة خاصة هي 258 عندما ينتهي الوقت المحدد. وتستخدم مصفوفة من Mutex objects عندما يكون لدينا عدد محدود من الموارد ونريد أن نربط كل واحد منها بمسار حالما يصبح ذلك المصدر متوفرا وفي هذه الحالة يصبح الـ Mutex objects الذي في حالة إشارة يعني أن المصدر الموافق متوفر عندئذ يمكنك استخدام الطريقة Mutex.WaitAny لمنع المسار الحالي حتى يصبح واحدا من الـ Mutex objects في حالة إشارة و النوع Mutex يرث الطريقة WaitAny من WaitHandle الخاصة بفئته الأب وهذا هيكل لتطبيق يستخدم هذه التقنية

كود :
' An array of three Mutex objects
Dim mutexes() As Mutex = {New Mutex(), New Mutex(), New Mutex()}

Sub WaitAnyExample()
' Wait until a resource becomes available.
' (Returns the index of the available resource.)
Dim mutexNdx As Integer = Mutex.WaitAny(mutexes)
' Enter the synchronized section.
' (This code should use only the resource corresponding to mutexNdx.)

' Exit the synchronized section, and release the resource.
mutexes(mutexNdx).ReleaseMutex()
End Sub
والطريقة الساكنة WaitAll أيضا موروثة من WaitHandle الخاصة بالفئة الأب حيث تأخذ مصفوفة من Mutex objects وتعيد التحكم للتطبيق فقط عندما يصبح جميعهم في حالة إشارة وهي مفيدة بشكل خاص عندما لا يمكنك المتابعة إلا عندما تكون جميع المسارات الباقية قد أنهت عملها

كود :
' Wait until all resources have been released.
Mutex.WaitAll(mutexes)
وهناك مشكلة صغيرة متعلقة بالطريقة WaitAll هي أنه لا يمكن استدعاؤها من المسار الرئيسي في تطبيق مسار الغرفة الوحيدة Single Thread Apartment (STA) application مثل تطبيق الكونسول Console application أو تطبيق نماذج ويندوز Windows Forms application ففي المسار الرئيسي لتطبيق STA يجب عليك التوقف حتى يتم تحرير مجموعة من الـ Mutex عندها يجب عليك استخدام WaitAll من مسار منفصل ثم استخدام الطريقة Thread.Join على ذلك المسار لإيقاف المسار الرئيسي حتى تعود الطريقة WaitAll وفي فيجول بايزيك 2005 والنسخة 2 من الفريموورك يوجد الطريقة الساكنة الجديدة SignalAndWait تمكنك من وضع Mutex object في حالة إشارة وانتظار Mutex object آخر

كود :
' Signal the first mutex and wait for the second mutex to become signaled.
Mutex.SignalAndWait(mutexes(0), mutexes(1))
وخلافا لجميع أغراض التزامن التي تم ذكرها حتى الآن يمكن لـ Mutex objects أن يرتبط باسم الأمر الذي يعد من أهم المزايا لهذه الأغراض فأغراض Mutex objects التي تمتلك نفس الاسم يمكن مشاركتها عبر العمليات ويمكنك إنشاء تواجد Instance لها كما يلي

كود :
Dim m As New Mutex(False, "mutexname")
وإن كان الاسم موجودا سابقا في النظام يحصل المستدعي على مرجع له وإلا سيتم إنشاء Mutex object جديد بحيث تمكنك هذه الآلية من مشاركة Mutex objects عبر عدة تطبيقات مختلفة وبهذا تتمكن هذه التطبيقات من مزامنة عمليات الوصول للمصادر المختلفة وقد تم إضافة باني جديد في الفريموورك 2 وفيجول بايزيك 2005 يمكنك من اختبار إذا كان قد تم منح المسار المستدعي ملكية الـ Mutex

كود :
Dim ownership As Boolean
Dim m As New Mutex(True, "mutexname", ownership)
If ownership Then
' This thread owns the mutex.

End If
من الاستخدامات الشائعة لـ named mutexes هو تحديد فيما إذا كان التطبيق العامل هو الأول أو الوحيد الذي تم تحميله وإن لم تكن هذه الحالة يمكن للتطبيق الخروج مباشرة أو الانتظار حتى تنتهي النسخة الأخرى من مهامها كما في المثال

كود :
Sub Main()
Dim ownership As Boolean
Dim m As New Mutex(True, "DemoMutex", ownership)
If ownership Then
Console.WriteLine("This app got the ownership of Mutex named DemoMutex")
Console.WriteLine("Press ENTER to run another instance of this app")
Console.ReadLine()
Process.Start(Assembly.GetExecutingAssembly().GetName().CodeBase)
Else
Console.WriteLine("This app is waiting to get ownership of Mutex named DemoMutex")
m.WaitOne()
End If
' Perform the task here.

Console.WriteLine("Press ENTER to release ownership of the mutex")
Console.ReadLine()
m.ReleaseMutex()
End Sub
والطريقة الساكنة OpenExisting جديدة أيضا في الفريموورك 2 وتقدم طريقة أخرى لفتح Mutex على مستوى النظام named system-wide Mutex object وبعكس باني الـ Mutex تمكنك هذه الطريقة من تحديد درجة التحكم التي تريدها على الـ Mutex

كود :
Try
' Request a mutex with the right to wait for it and to release it.
Dim rights As MutexRights = MutexRights.Synchronize Or MutexRights.Modify
Dim m As Mutex = Mutex.OpenExisting("mutexname", rights)
' Use the mutex here.

Catch ex As WaitHandleCannotBeOpenedException
' The specified object doesn't exist.
Catch ex As UnauthorizedAccessException
' The specified object exists, but current user doesn't have the
' necessary access rights.
Catch ex As IOException
' A Win32 error has occurred.
End Try
وفي فيجول بايزيك 2005 والفريموورك 2 تظهر الميزة الجديدة الأهم في النوع Mutex وهي إمكانية الوصول لقوائم التحكم بالوصول access control lists (ACLs) في النموذج عبر الغرض System.Security.AccessControl.MutexSecurity object حيث يمكنك تحديد ACL عندما تنشئ غرض Mutex جديد مستخدما الطريقة GetAccessControl للحصول على غرض MutexSecurity المرتبط بـ Mutex محدد وتطبيق ACL جديد باستخدام الطريقة SetAccessControl

كود :
Dim ownership As Boolean
Dim m As New Mutex(True, "mutexname", ownership)
If Not ownership Then
' Determine who is the owner of the mutex.
Dim mutexSec As MutexSecurity = m.GetAccessControl()
Dim account As NTAccount = DirectCast(mutexSec.GetOwner(GetType(NTAccount)), _
NTAccount)
Console.WriteLine("Mutex is owned by {0}", account)
End If
}}}
تم الشكر بواسطة:


الردود في هذا الموضوع
تزامن المسارات Thread Synchronization - بواسطة Raggi Tech - 03-10-12, 08:34 AM

المواضيع المحتمل أن تكون متشابهة .
الموضوع : الكاتب الردود : المشاهدات : آخر رد
  التعامل مع Resources و image list الاستغناء عن المسارات RaggiTech 1 3,492 02-10-12, 10:58 AM
آخر رد: RaggiTech
  استخدام بحيرة المسارات Using the Thread pool RaggiTech 0 2,301 02-10-12, 01:36 AM
آخر رد: RaggiTech
  Thread Methods &amp; Proprties &amp; Attributes RaggiTech 0 1,854 01-10-12, 07:33 PM
آخر رد: RaggiTech

التنقل السريع :


يقوم بقرائة الموضوع: