Link to home
Start Free TrialLog in
Avatar of mcainc
mcainc

asked on

VB.NET - Thread safe collections (use SyncLock or Synchronize?)

Ok this is becoming a huge pain, I can't figure out how to do this. I tried SyncLock but I must have done it wrong because I was still getting out of range exceptions.

I have a timer that fires every 1 second & loops through this collection to check some conditions... also have events that can be raised from other threads that can use / modify these collections the same way.

Here is an example of my code, how can I make this thread safe?

Dim OfficeCollection As New ArrayList

Public Structure OfficeData
    Dim firstname As String
    Dim lastname As String
End Structure

*** Timer Start
    Dim ODC As OfficeData
    ODC.firstname = "whatever"
    ODC.lastname = "whatever"

    For x = OfficeCollection.Count -1 to 0 Step -1
        ... some condition 1: OfficeCollection.Add(ODC)
        ... some condition 2: OfficeCollection.Remove(ODC)
    Next
Timer End ***

*** Event 1 Start
    Dim ODC As OfficeData
    ODC.firstname = "whatever"
    ODC.lastname = "whatever"

    For x = OfficeCollection.Count -1 to 0 Step -1
       ... conditions
        OfficeCollection.Add(ODC)
    Next
Event 1 End ***

*** Event 2 Start
    Dim ODC As OfficeData
    ODC.firstname = "whatever"
    ODC.lastname = "whatever"

    For x = OfficeCollection.Count -1 to 0 Step -1
       ... conditions
        OfficeCollection.Remove(ODC)
    Next
Event 2 End ***

I'm pretty frustrated at this point... I'm not 100% sure where to use SyncLock (around the for loop? only around .Add / .Remove calls? Both?)

What about  .Synchronize?

Thanks a ton
ASKER CERTIFIED SOLUTION
Avatar of Christopher Kile
Christopher Kile
Flag of United States of America image

Link to home
membership
This solution is only available to members.
To access this solution, you must be a member of Experts Exchange.
Start Free Trial
Avatar of mcainc
mcainc

ASKER

On MSDN in Thread Safety (for the ArrayList class) it says that the list is only thread safe if it isn't modified... thats where my problem is, events are modifying the list before reads have finished.

I guess I'm just not following exactly where to place these locks.. I appreciate your help, my head is mush right now :(
Avatar of mcainc

ASKER

** reading the list is only thread safe if it isn't modified.
http://msdn.microsoft.com/en-us/library/system.collections.icollection.syncroot.aspx
http://msdn.microsoft.com/en-us/library/system.collections.arraylist.issynchronized.aspx
The big problem seems to be when you're enumerating while something else is modifying the collection.  
Do you want a picture of the collection at the time you modified it, and don't care what changes take place after you take the intial picture? Then lock the list, copy it to a temporary working copy of the list, then release your lock.  Modification threads can then go to work.
Do you want your list to always reflect changes to it?  Then use do-while loops and forget enumerators.  Each time you fetch an item, lock the list while you fetch the item, then release the lock as soon as the fetch is complete.  When you add an item or remove an item, include a try-catch block that handles the exceptions which occur when the item is already in the list or when it has already been removed.  Don't use for loops, as sometimes the compiler will cache the end counter in a register at the start of the loop; always use a standalone if-then to check whether your current index is greater than the size of the list.
Try playing with some of these thoughts, after you take a break.  
Avatar of mcainc

ASKER

my list will change frequently during loops (the list is looped through every second by a timer)... so if at all possible. I would like to always be working with a list that reflects changes to it

I haven't thought to try a do while loop

i'm still having trouble wrapping my head around the way to structure that do while loop (as far as where to lock)

        Dim x As Integer = 0
        Do While x <> MyList.Count - 1
            SyncLock MySync.SyncObject
                ODC = MyList(x)
            End SyncLock
                ' perform task & remove item from MyList if condition met?
            x = x + 1
        Loop

or

        Dim x As Integer = 0
        SyncLock MySync.SyncObject
            Do While x <> MyList.Count - 1
                ODC = MyList(x)
                ' perform task & remove item from MyList if condition met?
                x = x + 1
            Loop
        End SyncLock

or am I still way off? (sorry still green when it comes to threading)

SOLUTION
Link to home
membership
This solution is only available to members.
To access this solution, you must be a member of Experts Exchange.
Start Free Trial
Avatar of mcainc

ASKER

ok so just to confirm, my code would be something like this:

Private Sub TimerFired() - happens every 1 second
    Dim x As Integer = 0
    SyncLock MySync.SyncObject
        Do While x <> MyList.Count - 1
            ODC = MyList(x)
            ... condition 1: AddToMyList(ODC)
            ... condition 2: RemoveFromMyList(x)
            x = x + 1
        Loop
    End SyncLock
End Sub

Private Sub AddToMyList(var)
    SyncLock MySync.SyncObject
        MyList.Add(var)
    End SyncLock
End Sub

Private Sub RemoveFromMyList(index)
    SyncLock MySync.SyncObject
        MyList.RemoveAt(index)
    End SyncLock
End Sub

should this do it? thanks guys!
Avatar of mcainc

ASKER

one more thing... this probably sounds silly but, with the synclocks in the add/remove subs... i should be able to call those subs freely from other methods without issues right?
That should do it.

You can still call the add/remove subs from anywhere but remember that now that you have "synchronized" the threads you have the potential for deadlocks.  Code carefully...  =)
http://msdn.microsoft.com/en-us/library/fxy8dte8(VS.71).aspx
Avatar of mcainc

ASKER

oh yay, this is going to be fun

thanks guys
You're very welcome :)