23-05-14, 10:19 AM
مقدمـــــــــــــــــــــــــة
منذ يومان كنت أقرأ هذا الموضوع في MSDN ...اللينـــــــــــــــــــــــك
و هو يتحدث عن استخدام Thread -Safe من خلال Collection تحديدا.
و لتوضيح الأمر قليلا انه و علي ما يبدو أن Collection الموجودة في الدوت نت قد لا تسترجع القيم الموجودة بها بشكل دقيق من خلال جملة For Each
و تلك حالة نادرة جدا ربما لا تحدث علي الاطلاق و خصوصا عندما تكون Collection ليس بها متغيرات ضخمة أو لا تكون بها أعداد ضخمة جدا من المتغيرات
و كما اوضحت مايكروسوفت أن استخدام ThreadSafe Collection يساعد علي أداء و سرعة أفضل لهذا النوع من Collections
لذلك قررت أن أكتب Collection الخاصة بي لكي أطبق من خلالها نفس هذا الاسلوب الموجود في موضوع مايكروسوفت.
ربما بعض المبرمجين لا يحبذ أن يكتب مثل هذا النوع من الكود و يفضل أن يستخدم بدلا منها الكلاسات الموجودة بالفعل في الدوت نت
لكن و بالرغم انني أهتم فقط بالجرافكس لكن مفهومي للأمر بسيط تماما
و هو أن كتابة مثل هذا النوع من الكود يعتبر تجربة جيدة و فريدة جدا لتطوير أداء و قدرات أي مبرمج و بالتالي هذا يؤدي الي زيادة فهمه للأمور بشكل أفضل
في الواقع لقد قد كتبت الكثير مثل هذا النوع من الكود تحديدا و لقد قمت ببناء أكثر من Generic Collection
لكن في تلك المرة قررت أن أستخدم Generic IList و في واقع الأمر كانت تلك المرة الأولي لي التي فيها اقوم بعمل Implementation لهذا Interface
و بالفعل كتبت الكلاس و أيضا كتبت Enumerator الخاص به و الكود كاملا موجود أدناه
ثم بعد ذلك قمت ب تجربة الكلاس الذي كتبته حتي أتأكد من أنه يعمل بصورة جيدة و هنا اكتشفت ان Collection الجديدة و بالرغم من أنها لا تعطي أي أخطاء علي الاظلاق عند استخدامها لكنها لا تسترجع أي قيمة بتاتا مع جملة For Each
كما هو واضح من الكود التالي
و راجعت الكود مرارا و تكرارا حتي أكتشف أين أخطات لكن بلا جدوي و لم أهتدي للمشكلة
و هنا أعدت كتابة الكلاس بشكل مختلف قليلا و قمت ب إلغاء Enumerator الذي كتبته و بدلا منه في جملة GetEnumerator استخدمت InnerList.GetEnumerator و هنا وجدت أن Collection تعمل بصورة جيدة واستطعت استرجاع جميع القيم منها بشكل طبيعي
و الكود التالي يوضخ شكل Collection بدون Enumerator الذي قمت أنا بكتابته
و الكود التالي يوضح كيفية استخدامها
طبعا لم أقتنع بذلك لأنني أريد أن أفهم ما هي المشكلة في الكود الذي كتبته و أخذت أبحث هنا و هناك علي الانترنت حتي اهتديت الي الجواب بعد أن أضعت أكثر من حوالي ثلاثة ساعات أقرأ فيها كل ما هو له صلة بهذا الأمر
و الكود التالي يوضح كيفية استخدام Collection التي أنا كتبتها و بشكل صحيح
الواضح هنا لي أنه علي ما يبدو أن Generic IList لا تدعم ThreadSafe و لذلك و من الافضل أن نستخدم بدلا منها Generic ICollection
هذا ما قرأته لكنني و حتي هذه اللحظة لم أتأكد من هذا الجواب 100%
لأنني في حاجة الي اعادة كتابة كل شئ باستخدام Generic ICollection بدلا من Generic IList
ربما أعيد كتابة Collection الخاصة بي مرة ثانية و أضع لكم الكود هنا لاحقا أو في المدونة الخاصة بي
أتمني أن يكون هذا الحوار البسيط مفيدا لكم
ستجدون الكود كاملا في المرفقات بنسخة الفيجوال استوديو 2012
تقبلوا تحياتي
منذ يومان كنت أقرأ هذا الموضوع في MSDN ...اللينـــــــــــــــــــــــك
و هو يتحدث عن استخدام Thread -Safe من خلال Collection تحديدا.
و لتوضيح الأمر قليلا انه و علي ما يبدو أن Collection الموجودة في الدوت نت قد لا تسترجع القيم الموجودة بها بشكل دقيق من خلال جملة For Each
و تلك حالة نادرة جدا ربما لا تحدث علي الاطلاق و خصوصا عندما تكون Collection ليس بها متغيرات ضخمة أو لا تكون بها أعداد ضخمة جدا من المتغيرات
و كما اوضحت مايكروسوفت أن استخدام ThreadSafe Collection يساعد علي أداء و سرعة أفضل لهذا النوع من Collections
لذلك قررت أن أكتب Collection الخاصة بي لكي أطبق من خلالها نفس هذا الاسلوب الموجود في موضوع مايكروسوفت.
ربما بعض المبرمجين لا يحبذ أن يكتب مثل هذا النوع من الكود و يفضل أن يستخدم بدلا منها الكلاسات الموجودة بالفعل في الدوت نت
لكن و بالرغم انني أهتم فقط بالجرافكس لكن مفهومي للأمر بسيط تماما
و هو أن كتابة مثل هذا النوع من الكود يعتبر تجربة جيدة و فريدة جدا لتطوير أداء و قدرات أي مبرمج و بالتالي هذا يؤدي الي زيادة فهمه للأمور بشكل أفضل
في الواقع لقد قد كتبت الكثير مثل هذا النوع من الكود تحديدا و لقد قمت ببناء أكثر من Generic Collection
لكن في تلك المرة قررت أن أستخدم Generic IList و في واقع الأمر كانت تلك المرة الأولي لي التي فيها اقوم بعمل Implementation لهذا Interface
و بالفعل كتبت الكلاس و أيضا كتبت Enumerator الخاص به و الكود كاملا موجود أدناه
كود :
Public Class RiverNileThreadSafeList(Of T)
Implements IList(Of T)
#Region " RiverNileThreadSafeList (of T) "
#Region " Field "
Friend _lock As Object = CType(Nothing, Object)
Friend _list As List(Of T) = CType(Nothing, List(Of T))
#End Region
#Region " Constructor "
Public Sub New()
Me.New(New Object)
End Sub
Friend Sub New(obj As Object)
Me._lock = obj
End Sub
#End Region
#Region " Property "
Public ReadOnly Property InnerList As List(Of T)
Get
SyncLock Me._lock
If Me._list Is Nothing Then
Me._list = New List(Of T)()
End If
End SyncLock
Return Me._list
End Get
End Property
Default Public Property Item(index As Integer) As T Implements IList(Of T).Item
Get
Dim value As T = CType(Nothing, T)
SyncLock Me._lock
value = Me.InnerList(index)
End SyncLock
Return value
End Get
Set(value As T)
SyncLock Me._lock
Me.InnerList(index) = value
End SyncLock
End Set
End Property
Public ReadOnly Property Count As Integer Implements ICollection(Of T).Count
Get
Dim value As Integer = CType(Nothing, Integer)
SyncLock Me._lock
value = Me.InnerList.Count
End SyncLock
Return value
End Get
End Property
Public ReadOnly Property IsReadOnly As Boolean Implements ICollection(Of T).IsReadOnly
Get
Dim value As Boolean = CType(Nothing, Boolean)
SyncLock Me._lock
value = False
End SyncLock
Return value
End Get
End Property
#End Region
#Region " Method "
Public Sub ForEach(action As Action(Of T))
SyncLock Me._lock
For Each item As Object In Me
action(item)
Next
End SyncLock
End Sub
Public Function Where(predicate As Func(Of T, Boolean)) As RiverNileThreadSafeList(Of T)
SyncLock Me._lock
Return New RiverNileThreadSafeList(Of T)(CType(Me._list, IEnumerable(Of T)).Where(predicate).ToList())
End SyncLock
End Function
Public Function FirstOrDefault(predicate As Func(Of T, Boolean)) As T
SyncLock Me._lock
Return CType(_list, IEnumerable(Of T)).FirstOrDefault(predicate)
End SyncLock
End Function
Public Sub AddRange(collection As IEnumerable(Of T))
SyncLock Me._lock
Me.InnerList.AddRange(collection)
End SyncLock
End Sub
Public Sub Add(item As T) Implements ICollection(Of T).Add
SyncLock Me._lock
Me.InnerList.Add(item)
End SyncLock
End Sub
Public Sub Clear() Implements ICollection(Of T).Clear
SyncLock Me._lock
Me.InnerList.Clear()
End SyncLock
End Sub
Public Function Contains(item As T) As Boolean Implements ICollection(Of T).Contains
Dim value As Boolean = CType(Nothing, Boolean)
SyncLock Me._lock
value = Me.InnerList.Contains(item)
End SyncLock
Return value
End Function
Public Sub CopyTo(array() As T, arrayIndex As Integer) Implements ICollection(Of T).CopyTo
SyncLock Me._lock
Me.InnerList.CopyTo(array, arrayIndex)
End SyncLock
End Sub
Public Sub CopyTo(array As T())
SyncLock Me._lock
Me.InnerList.CopyTo(array)
End SyncLock
End Sub
Public Function Remove(item As T) As Boolean Implements ICollection(Of T).Remove
Dim value As Boolean = CType(Nothing, Boolean)
SyncLock Me._lock
value = Me.InnerList.Remove(item)
End SyncLock
Return value
End Function
Public Function IndexOf(item As T) As Integer Implements IList(Of T).IndexOf
Dim value As Integer = CType(Nothing, Integer)
SyncLock Me._lock
value = Me.InnerList.IndexOf(item)
End SyncLock
Return value
End Function
Public Sub Insert(index As Integer, item As T) Implements IList(Of T).Insert
SyncLock Me._lock
Me.InnerList.Insert(index, item)
End SyncLock
End Sub
Public Sub RemoveAt(index As Integer) Implements IList(Of T).RemoveAt
SyncLock Me._lock
Me.InnerList.RemoveAt(index)
End SyncLock
End Sub
Private Function CreateCairoEnumerator() As IEnumerator(Of T)
Return New RiverNileThreadSafeListEnumerator(Me)
End Function
Public Function GetEnumerator() As IEnumerator(Of T) Implements IEnumerable(Of T).GetEnumerator
Dim value As IEnumerator(Of T) = CType(Nothing, IEnumerator(Of T))
SyncLock Me._lock
' Why My collection not respect my Enumerator.......??????????????
value = New RiverNileThreadSafeListEnumerator(Me)
' the collection works fine with this value
'value = Me.InnerList.GetEnumerator()
End SyncLock
Return value
End Function
Private Function GetEnumerator1() As IEnumerator Implements IEnumerable.GetEnumerator
Dim value As IEnumerator = CType(Nothing, IEnumerator)
SyncLock Me._lock
value = New RiverNileThreadSafeListEnumerator(Me)
'value = Me.InnerList.GetEnumerator()
End SyncLock
Return value
End Function
#End Region
#End Region
#Region " My Enumerator "
Friend Class RiverNileThreadSafeListEnumerator
Implements IEnumerator(Of T)
Private _list As RiverNileThreadSafeList(Of T)
Private _enumerator As IEnumerator(Of T)
Public Sub New(list As RiverNileThreadSafeList(Of T))
SyncLock Me._list._lock
Me._list = New RiverNileThreadSafeList(Of T)()
Me._list.AddRange(list)
Me._enumerator = Me._list.InnerList.GetEnumerator()
End SyncLock
End Sub
Public ReadOnly Property Current As T Implements IEnumerator(Of T).Current
Get
Dim value As T = CType(Nothing, T)
SyncLock Me._list._lock
value = Me._enumerator.Current
End SyncLock
Return value
End Get
End Property
Private ReadOnly Property Current1 As Object Implements IEnumerator.Current
Get
Dim value As Object = CType(Nothing, Object)
SyncLock Me._list._lock
value = Me._enumerator.Current
End SyncLock
Return value
End Get
End Property
Public Function MoveNext() As Boolean Implements IEnumerator.MoveNext
Dim result As Boolean = CType(Nothing, Boolean)
SyncLock Me._list._lock
result = Me._enumerator.MoveNext()
End SyncLock
Return result
End Function
Public Sub Reset() Implements IEnumerator.Reset
SyncLock Me._list._lock
Me._enumerator.Reset()
End SyncLock
End Sub
Public Sub Dispose() Implements IDisposable.Dispose
SyncLock Me._list._lock
Me._enumerator.Dispose()
End SyncLock
End Sub
End Class
#End Region
End Class ' RiverNileThreadSafeList(Of T)
ثم بعد ذلك قمت ب تجربة الكلاس الذي كتبته حتي أتأكد من أنه يعمل بصورة جيدة و هنا اكتشفت ان Collection الجديدة و بالرغم من أنها لا تعطي أي أخطاء علي الاظلاق عند استخدامها لكنها لا تسترجع أي قيمة بتاتا مع جملة For Each
كما هو واضح من الكود التالي
كود :
Public Class Test_Form
Private ColorList As RiverNileThreadSafeList(Of KnownColor) = New RiverNileThreadSafeList(Of KnownColor)() From {KnownColor.ActiveCaption, KnownColor.Aqua, KnownColor.YellowGreen}
Private Sub Form1_Load(sender As Object, e As EventArgs) Handles MyBase.Load
' Test my collection with my Enumerator
' in this case no value will be add to the list box
For Each clr As KnownColor In ColorList
ListBox2.Items.Add(clr)
Next
End Sub
End Class
و راجعت الكود مرارا و تكرارا حتي أكتشف أين أخطات لكن بلا جدوي و لم أهتدي للمشكلة
و هنا أعدت كتابة الكلاس بشكل مختلف قليلا و قمت ب إلغاء Enumerator الذي كتبته و بدلا منه في جملة GetEnumerator استخدمت InnerList.GetEnumerator و هنا وجدت أن Collection تعمل بصورة جيدة واستطعت استرجاع جميع القيم منها بشكل طبيعي
و الكود التالي يوضخ شكل Collection بدون Enumerator الذي قمت أنا بكتابته
كود :
Public Class SafeListWithoutEnumerator(Of T)
Implements IList(Of T)
#Region " SafeListWithoutEnumerator (of T) "
#Region " Field "
Friend _lock As Object = CType(Nothing, Object)
Friend _list As List(Of T) = CType(Nothing, List(Of T))
#End Region
#Region " Constructor "
Public Sub New()
Me.New(New Object)
End Sub
Friend Sub New(obj As Object)
Me._lock = obj
End Sub
#End Region
#Region " Property "
Public ReadOnly Property InnerList As List(Of T)
Get
SyncLock Me._lock
If Me._list Is Nothing Then
Me._list = New List(Of T)()
End If
End SyncLock
Return Me._list
End Get
End Property
Default Public Property Item(index As Integer) As T Implements IList(Of T).Item
Get
Dim value As T = CType(Nothing, T)
SyncLock Me._lock
value = Me.InnerList(index)
End SyncLock
Return value
End Get
Set(value As T)
SyncLock Me._lock
Me.InnerList(index) = value
End SyncLock
End Set
End Property
Public ReadOnly Property Count As Integer Implements ICollection(Of T).Count
Get
Dim value As Integer = CType(Nothing, Integer)
SyncLock Me._lock
value = Me.InnerList.Count
End SyncLock
Return value
End Get
End Property
Public ReadOnly Property IsReadOnly As Boolean Implements ICollection(Of T).IsReadOnly
Get
Dim value As Boolean = CType(Nothing, Boolean)
SyncLock Me._lock
value = False
End SyncLock
Return value
End Get
End Property
#End Region
#Region " Method "
Public Sub ForEach(action As Action(Of T))
SyncLock Me._lock
For Each item As Object In Me
action(item)
Next
End SyncLock
End Sub
Public Function Where(predicate As Func(Of T, Boolean)) As SafeListWithoutEnumerator(Of T)
SyncLock Me._lock
Return New SafeListWithoutEnumerator(Of T)(CType(Me._list, IEnumerable(Of T)).Where(predicate).ToList())
End SyncLock
End Function
Public Function FirstOrDefault(predicate As Func(Of T, Boolean)) As T
SyncLock Me._lock
Return CType(_list, IEnumerable(Of T)).FirstOrDefault(predicate)
End SyncLock
End Function
Public Sub AddRange(collection As IEnumerable(Of T))
SyncLock Me._lock
Me.InnerList.AddRange(collection)
End SyncLock
End Sub
Public Sub Add(item As T) Implements ICollection(Of T).Add
SyncLock Me._lock
Me.InnerList.Add(item)
End SyncLock
End Sub
Public Sub Clear() Implements ICollection(Of T).Clear
SyncLock Me._lock
Me.InnerList.Clear()
End SyncLock
End Sub
Public Function Contains(item As T) As Boolean Implements ICollection(Of T).Contains
Dim value As Boolean = CType(Nothing, Boolean)
SyncLock Me._lock
value = Me.InnerList.Contains(item)
End SyncLock
Return value
End Function
Public Sub CopyTo(array() As T, arrayIndex As Integer) Implements ICollection(Of T).CopyTo
SyncLock Me._lock
Me.InnerList.CopyTo(array, arrayIndex)
End SyncLock
End Sub
Public Sub CopyTo(array As T())
SyncLock Me._lock
Me.InnerList.CopyTo(array)
End SyncLock
End Sub
Public Function Remove(item As T) As Boolean Implements ICollection(Of T).Remove
Dim value As Boolean = CType(Nothing, Boolean)
SyncLock Me._lock
value = Me.InnerList.Remove(item)
End SyncLock
Return value
End Function
Public Function IndexOf(item As T) As Integer Implements IList(Of T).IndexOf
Dim value As Integer = CType(Nothing, Integer)
SyncLock Me._lock
value = Me.InnerList.IndexOf(item)
End SyncLock
Return value
End Function
Public Sub Insert(index As Integer, item As T) Implements IList(Of T).Insert
SyncLock Me._lock
Me.InnerList.Insert(index, item)
End SyncLock
End Sub
Public Sub RemoveAt(index As Integer) Implements IList(Of T).RemoveAt
SyncLock Me._lock
Me.InnerList.RemoveAt(index)
End SyncLock
End Sub
Public Function GetEnumerator() As IEnumerator(Of T) Implements IEnumerable(Of T).GetEnumerator
Dim value As IEnumerator(Of T) = CType(Nothing, IEnumerator(Of T))
SyncLock Me._lock
' works fine with this value
value = Me.InnerList.GetEnumerator()
End SyncLock
Return value
End Function
Private Function GetEnumerator1() As IEnumerator Implements IEnumerable.GetEnumerator
Dim value As IEnumerator = CType(Nothing, IEnumerator)
SyncLock Me._lock
value = Me.InnerList.GetEnumerator()
End SyncLock
Return value
End Function
#End Region
#End Region
End Class ' SafeListWithoutEnumerator (of T)
و الكود التالي يوضح كيفية استخدامها
كود :
Public Class Test_Form
Private KnownColorList As SafeListWithoutEnumerator(Of KnownColor) = New SafeListWithoutEnumerator(Of KnownColor)() From {KnownColor.Blue, KnownColor.ButtonFace, KnownColor.CadetBlue}
Private Sub Form1_Load(sender As Object, e As EventArgs) Handles MyBase.Load
' test colloection which has now written enumerator
For Each item In KnownColorList
ListBox1.Items.Add(item)
Next
End Sub
End Class
طبعا لم أقتنع بذلك لأنني أريد أن أفهم ما هي المشكلة في الكود الذي كتبته و أخذت أبحث هنا و هناك علي الانترنت حتي اهتديت الي الجواب بعد أن أضعت أكثر من حوالي ثلاثة ساعات أقرأ فيها كل ما هو له صلة بهذا الأمر
و الكود التالي يوضح كيفية استخدام Collection التي أنا كتبتها و بشكل صحيح
كود :
Public Class Test_Form
Private ColorList As RiverNileThreadSafeList(Of KnownColor) = New RiverNileThreadSafeList(Of KnownColor)() From {KnownColor.ActiveCaption, KnownColor.Aqua, KnownColor.YellowGreen}
Private KnownColorList As SafeListWithoutEnumerator(Of KnownColor) = New SafeListWithoutEnumerator(Of KnownColor)() From {KnownColor.Blue, KnownColor.ButtonFace, KnownColor.CadetBlue}
Private Sub Form1_Load(sender As Object, e As EventArgs) Handles MyBase.Load
' test colloection which has now written enumerator
For Each item In KnownColorList
ListBox1.Items.Add(item)
Next
' the correct way to use my collection
' Test my collection with my Enumerator
' in this case values will be added to my Collectio
For Each clr As KnownColor In ColorList.ToArray()
ListBox2.Items.Add(clr)
Next
End Sub
End Class
الواضح هنا لي أنه علي ما يبدو أن Generic IList لا تدعم ThreadSafe و لذلك و من الافضل أن نستخدم بدلا منها Generic ICollection
هذا ما قرأته لكنني و حتي هذه اللحظة لم أتأكد من هذا الجواب 100%
لأنني في حاجة الي اعادة كتابة كل شئ باستخدام Generic ICollection بدلا من Generic IList
ربما أعيد كتابة Collection الخاصة بي مرة ثانية و أضع لكم الكود هنا لاحقا أو في المدونة الخاصة بي
أتمني أن يكون هذا الحوار البسيط مفيدا لكم
ستجدون الكود كاملا في المرفقات بنسخة الفيجوال استوديو 2012
تقبلوا تحياتي