03-10-12, 08:34 AM
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عمليات القراءة والكتابة المتغيرة 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كود :
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 ClassThe 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كود :
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يوفر النوع 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كود :
' 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كود :
' 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كود :
' Wait until all resources have been released.
Mutex.WaitAll(mutexes)كود :
' Signal the first mutex and wait for the second mutex to become signaled.
Mutex.SignalAndWait(mutexes(0), mutexes(1))كود :
Dim m As New Mutex(False, "mutexname")كود :
Dim ownership As Boolean
Dim m As New Mutex(True, "mutexname", ownership)
If ownership Then
' This thread owns the mutex.
…
End Ifكود :
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كود :
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كود :
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