Results 1 to 6 of 6

Thread: [2008] [WORKED AROUND] SyncLock question

Threaded View

  1. #1

    Thread Starter
    Frenzied Member ntg's Avatar
    Join Date
    Sep 2004
    Posts
    1,449

    Exclamation [2008] [WORKED AROUND] SyncLock question

    I have the following two clases:
    VB Code:
    1. ''' <summary>
    2. ''' This class implements a list with keys that expire
    3. ''' after a certain amount of time.
    4. ''' </summary>
    5. ''' <remarks>The timed list holds keys to items stored elsewhere.</remarks>
    6. Public Class TimedList
    7.  
    8.     ''' <summary>
    9.     ''' Event raised when a key has expired.
    10.     ''' </summary>
    11.     ''' <param name="sender"></param>
    12.     ''' <param name="itemKey"></param>
    13.     ''' <remarks></remarks>
    14.     Public Event ItemExpired(ByVal sender As TimedList, ByVal itemKey As String)
    15.  
    16.     ''' <summary>
    17.     ''' List with items.
    18.     ''' </summary>
    19.     ''' <remarks></remarks>
    20.     Protected m_list As SortedList(Of String, TimedItem)
    21.  
    22.     ''' <summary>
    23.     ''' Our pulse timer.
    24.     ''' </summary>
    25.     ''' <remarks></remarks>
    26.     Protected WithEvents m_timer As System.Timers.Timer
    27.  
    28.     ''' <summary>
    29.     ''' Lock object.
    30.     ''' </summary>
    31.     ''' <remarks></remarks>
    32.     Protected Shared m_lock As New Queue
    33.  
    34.     ''' <summary>
    35.     ''' Default class constructor.
    36.     ''' </summary>
    37.     ''' <remarks></remarks>
    38.     Public Sub New()
    39.         m_list = New SortedList(Of String, TimedItem)
    40.     End Sub
    41.  
    42.     ''' <summary>
    43.     ''' Starts our timer.
    44.     ''' </summary>
    45.     ''' <param name="sweepInterval">Sweep interval in seconds.</param>
    46.     ''' <remarks></remarks>
    47.     Public Sub StartTimer(ByVal sweepInterval As Integer)
    48.         m_timer = New Timers.Timer(sweepInterval * 1000)
    49.         m_timer.AutoReset = True
    50.         m_timer.Start()
    51.     End Sub
    52.  
    53.     ''' <summary>
    54.     ''' Stops the timer.
    55.     ''' </summary>
    56.     ''' <remarks></remarks>
    57.     Public Sub StopTimer()
    58.         SyncLock queue.Synchronized(m_lock)
    59.             m_timer.Stop()
    60.             m_timer.Dispose()
    61.             m_timer = Nothing
    62.         End SyncLock
    63.     End Sub
    64.  
    65.     ''' <summary>
    66.     ''' Adds a new key to the timed list.
    67.     ''' </summary>
    68.     ''' <param name="key">Key to add.</param>
    69.     ''' <param name="expirySeconds">Seconds after which the item expires.</param>
    70.     ''' <remarks></remarks>
    71.     Public Sub AddItem(ByVal key As String, ByVal expirySeconds As Integer)
    72.         SyncLock queue.Synchronized(m_lock)
    73.             m_list.Add(key, New TimedItem(key, Now.AddSeconds(expirySeconds)))
    74.         End SyncLock
    75.     End Sub
    76.  
    77.     ''' <summary>
    78.     ''' Removes a key from the list.
    79.     ''' </summary>
    80.     ''' <param name="key"></param>
    81.     ''' <remarks></remarks>
    82.     Public Sub RemoveItem(ByVal key As String)
    83.         SyncLock queue.Synchronized(m_lock)
    84.             m_list.Remove(key)
    85.         End SyncLock
    86.     End Sub
    87.  
    88.     ''' <summary>
    89.     ''' Renews a keys' lifetime.
    90.     ''' </summary>
    91.     ''' <param name="key"></param>
    92.     ''' <param name="expirySeconds"></param>
    93.     ''' <remarks></remarks>
    94.     Public Sub RenewItem(ByVal key As String, ByVal expirySeconds As Integer)
    95.         SyncLock queue.Synchronized(m_lock)
    96.             If m_list.ContainsKey(key) = False Then
    97.                 Throw New Exception("Item with key " + key + " was not found")
    98.             End If
    99.             m_list(key).Expiry = Now.AddSeconds(expirySeconds)
    100.         End SyncLock
    101.     End Sub
    102.  
    103.     ''' <summary>
    104.     ''' Clears all items in the list.
    105.     ''' </summary>
    106.     ''' <remarks></remarks>
    107.     Public Sub Cleanup()
    108.         SyncLock queue.Synchronized(m_lock)
    109.             m_list.Clear()
    110.         End SyncLock
    111.     End Sub
    112.  
    113.     ''' <summary>
    114.     ''' Called when the timer elapses.
    115.     ''' </summary>
    116.     ''' <param name="sender"></param>
    117.     ''' <param name="e"></param>
    118.     ''' <remarks></remarks>
    119.     Private Sub m_timer_Elapsed(ByVal sender As Object, ByVal e As System.Timers.ElapsedEventArgs) Handles m_timer.Elapsed
    120.         m_timer.Stop()
    121.  
    122.         Dim remList As New List(Of String), en As IEnumerator(Of KeyValuePair(Of String, TimedItem)) = Nothing
    123.  
    124.         SyncLock m_lock.SyncRoot
    125.             Try
    126.                 en = m_list.GetEnumerator()
    127.                 en.Reset()
    128.                 While en.MoveNext
    129.                     If en.Current.Value.HasExpired Then
    130.                         Try
    131.                             RaiseEvent ItemExpired(Me, en.Current.Key)
    132.                         Catch ex As Exception
    133.                             Debug.WriteLine("An exception has occurred while notifying caller that an item has expired (" + ex.ToString + ")")
    134.                         End Try
    135.                         remList.Add(en.Current.Key)
    136.                     End If
    137.                 End While
    138.             Catch ex As Exception
    139.                 Debug.WriteLine("+=====================================================================+")
    140.                 Debug.WriteLine("TimedList exception")
    141.                 Debug.WriteLine(ex.ToString)
    142.                 Debug.WriteLine("+=====================================================================+")
    143.             Finally
    144.                 If en IsNot Nothing Then
    145.                     en.Dispose()
    146.                     en = Nothing
    147.                 End If
    148.                 For Each key As String In remList
    149.                     m_list.Remove(key)
    150.                 Next
    151.                 remList = Nothing
    152.             End Try
    153.         End SyncLock
    154.  
    155.         m_timer.Start()
    156.  
    157.     End Sub
    158.  
    159. End Class
    160.  
    161. ''' <summary>
    162. ''' This class is used to represent a timed item.
    163. ''' </summary>
    164. ''' <remarks></remarks>
    165. Public Class TimedItem
    166.     Implements IComparable(Of TimedItem)
    167.  
    168.     ''' <summary>
    169.     ''' Item key.
    170.     ''' </summary>
    171.     ''' <remarks></remarks>
    172.     Protected m_key As String
    173.  
    174.     ''' <summary>
    175.     ''' Item expiry.
    176.     ''' </summary>
    177.     ''' <remarks></remarks>
    178.     Protected m_expiry As DateTime
    179.  
    180.     ''' <summary>
    181.     ''' Get/set the item key.
    182.     ''' </summary>
    183.     ''' <value></value>
    184.     ''' <returns></returns>
    185.     ''' <remarks></remarks>
    186.     Public Property Key() As String
    187.         Get
    188.             Return m_key
    189.         End Get
    190.         Set(ByVal value As String)
    191.             m_key = value
    192.         End Set
    193.     End Property
    194.  
    195.     ''' <summary>
    196.     ''' Get/set the item expiry.
    197.     ''' </summary>
    198.     ''' <value></value>
    199.     ''' <returns></returns>
    200.     ''' <remarks></remarks>
    201.     Public Property Expiry() As DateTime
    202.         Get
    203.             Return m_expiry
    204.         End Get
    205.         Set(ByVal value As DateTime)
    206.             m_expiry = value
    207.         End Set
    208.     End Property
    209.  
    210.     ''' <summary>
    211.     ''' Default class constructor.
    212.     ''' </summary>
    213.     ''' <param name="key">Item key.</param>
    214.     ''' <param name="expiry">Item expiry.</param>
    215.     ''' <remarks></remarks>
    216.     Public Sub New(ByVal key As String, ByVal expiry As DateTime)
    217.         Me.Key = key
    218.         Me.Expiry = expiry
    219.     End Sub
    220.  
    221.     ''' <summary>
    222.     ''' Returns True if the item has expired, False otherwise.
    223.     ''' </summary>
    224.     ''' <returns></returns>
    225.     ''' <remarks></remarks>
    226.     Public Function HasExpired() As Boolean
    227.         Return (DateTime.Compare(Now, m_expiry) >= 0)
    228.     End Function
    229.  
    230.     ''' <summary>
    231.     ''' Implemented to allow for sort/search operations.
    232.     ''' </summary>
    233.     ''' <param name="other">Other instance of TimedItem.</param>
    234.     ''' <returns></returns>
    235.     ''' <remarks></remarks>
    236.     Public Function CompareTo(ByVal other As TimedItem) As Integer Implements System.IComparable(Of TimedItem).CompareTo
    237.         Return String.Compare(Me.Key, other.Key)
    238.     End Function
    239.  
    240. End Class
    The purpose of TimedList is to have a list that holds items that expire after some time. When that happens, the TimedList fires an event to request any cleanup and then removes the TimedItem from its list. The user of TimedList is bound to add and remove items to the list in a dynamic manner so there is a lock object used to make sure that sensitive operations (list addition and enumeration) are performed one at a time. The items of the TimedList are internally stored in a sorted list which has a key to each item.

    The problem I have is beginning to manifest itself when I make heavy use of a TimedList (I have only one instance of it). When I throw at it items that expire after 5 seconds at the rate of 100 per second for two minutes, I get the following exception:

    System.InvalidOperationException: Collection was modified after the enumerator was instantiated.
    at System.ThrowHelper.ThrowInvalidOperationException(ExceptionResource resource)
    at System.Collections.Generic.SortedList`2.Enumerator.MoveNext()
    at RF.Various.TimedList.m_timer_Elapsed(Object sender, ElapsedEventArgs e)
    I currently have code that works around the issue but I'd much prefer to eradicate the real problem. I can't seem to find anything wrong with my use of SyncLock but note that I'm locking on a different object than the sorted list I'm using. Any ideas anyone?
    Last edited by ntg; Aug 14th, 2008 at 02:44 AM.
    "Feel the force...read the source..."
    Utilities: POPFileDebugViewProcess ExplorerWiresharkKeePassUltraVNCPic2Ascii
    .Net tools & open source: DotNetNukelog4NetCLRProfiler
    My open source projects: Thales SimulatorEFT CalculatorSystem Info ReporterVSS2SVNIBAN Functions
    Customer quote: "If the server has a RAID array, why should we bother with backups?"
    Programmer quote: "I never comment my code. Something that is hard to write should be impossible to comprehend."
    Ignorant quote: "I have no respect for universities, as they teach not practicle stuff, and charge money for"

Posting Permissions

  • You may not post new threads
  • You may not post replies
  • You may not post attachments
  • You may not edit your posts
  •  



Click Here to Expand Forum to Full Width