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
mcaincAsked:
Who is Participating?
I wear a lot of hats...

"The solutions and answers provided on Experts Exchange have been extremely helpful to me over the last few years. I wear a lot of hats - Developer, Database Administrator, Help Desk, etc., so I know a lot of things but not a lot about one thing. Experts Exchange gives me answers from people who do know a lot about one thing, in a easy to use platform." -Todd S.

Christopher KileCommented:
Why reinvent the wheel? :)
Check out this previous solution and the links it provides, then if you need more help write back.
http://www.experts-exchange.com/Programming/Languages/.NET/Visual_Basic.NET/Q_23867613.html
 
0

Experts Exchange Solution brought to you by

Your issues matter to us.

Facing a tech roadblock? Get the help and guidance you need from experienced professionals who care. Ask your question anytime, anywhere, with no hassle.

Start your 7-day free trial
mcaincAuthor Commented:
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 :(
0
mcaincAuthor Commented:
** reading the list is only thread safe if it isn't modified.
0
Cloud Class® Course: Python 3 Fundamentals

This course will teach participants about installing and configuring Python, syntax, importing, statements, types, strings, booleans, files, lists, tuples, comprehensions, functions, and classes.

Christopher KileCommented:
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.  
0
mcaincAuthor Commented:
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)

0
Mike TomlinsonMiddle School Assistant TeacherCommented:
You want the second version.

Basically you need to use SyncLock around the entire looping structure and around the add/remove methods.

But just so we are clear...if implemented like this it will actually PREVENT changes completely until the looping blocks have completed.  The other threads will wait for the loops to finish before they can modify the collection.
0
mcaincAuthor Commented:
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!
0
mcaincAuthor Commented:
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?
0
Mike TomlinsonMiddle School Assistant TeacherCommented:
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
0
mcaincAuthor Commented:
oh yay, this is going to be fun

thanks guys
0
Christopher KileCommented:
You're very welcome :)
0
It's more than this solution.Get answers and train to solve all your tech problems - anytime, anywhere.Try it for free Edge Out The Competitionfor your dream job with proven skills and certifications.Get started today Stand Outas the employee with proven skills.Start learning today for free Move Your Career Forwardwith certification training in the latest technologies.Start your trial today
Visual Basic.NET

From novice to tech pro — start learning today.