ArrayList in Thread question

F

fniles

I am using VB.NET 2003 and a socket control to receive and sending data to
clients.
As I receive data in 1 thread, I put it into an arraylist, and then I remove
the data from arraylist and send it to the client.
Before adding data to the arraylist, I check if the depth of the arraylist
is longer than iMaxQueueDepth, and if it is, I clear the arraylist.
Is it possible that while I am clearing the arraylist, the ThreadMain at the
same time also trying to remove the data in the arraylist ?
In other words, in the following codes, in the Sub NewQuote doing
QuotesSync.Add(Message) at the same time ThreadMain doing
sPacket = QuotesSync(0)
QuotesSync.RemoveAt(0)
?

Thank you.

Public Class ListenerClass
ClientSession = New SessionClass
' Create a new worker thread to handle the client session
ClientThread = New Threading.Thread(AddressOf ClientSession.ThreadMain)
end class

Public Class frmQuoteServer
Public Function RegisterClient(ByRef Session As SessionClass, ByRef Thread
As Threading.Thread) As Boolean
ReDim Preserve Client(LastClient)
client(LastClient).Session = Session
Client(LastClient).Thread = Thread
end Function

Sub ProcessEachMessage
'This sub is called by another thread that receives quotes
For lIndex = 0 To LastClient - 1
'assigning the quote to the client session
Client(lIndex).Session.NewQuote(sStr)
next
end sub
end Class

Public Class SessionClass
Public Server As frmQuoteServer
Private Quotes As New ArrayList
Private QuotesSync As ArrayList = ArrayList.Synchronized(Quotes)

Sub NewQuote(ByVal Message As String)
'this sub is called a receiving data thread receives data
If QuotesSync.Count > iMaxQueueDepth Then QuotesSync.Clear() 'clear the
queue if it is longer than iMaxQueueDepth
QuotesSync.Add(Message)
end Sub

Public Sub ThreadMain()
If Server.RegisterClient(Me, Thread.CurrentThread()) = False Then
:
Exit Sub
End If

'this client thread is looping all the time until it is terminated
Do While Not Terminated
sPacket = ""
While QuotesSync.Count > 0 And Not (Terminated)
sPacket = QuotesSync(0)
nResult = Socket.Write(sPacket)
QuotesSync.RemoveAt(0)
End While
Thread.Sleep(1)
Loop
end Sub
end class
 
F

fniles

Thank you for your reply.
Do I understand it correctly that when I synchronized an arraylist, it will
be thread safe and it won't throw an exception even when other threads
modify the arraylist, but when I synchronized a collection it will still
throw an exception ?
 
G

Guest

fniles,

Here the documentation is using the term "collection" to refer to all
collections, including arraylist collections.

Kerry Moorman
 
F

fniles

THanks.If I lock the arraylist, and another thread try to modify it, what will
happen there ?
 
T

Tom Shelton

THanks.
If I lock the arraylist, and another thread try to modify it, what will
happen there ?

There are a lot of issues with threading and collections. You have to be very
careful - even with a syncronized wrapper. The fact, is that lots of stuff
can happen in a multi-threaded program in orders that you do not intend.. for
example:

sub somesub ' run on a thread
arraylist.clear ()
end sub


function acoolfunction 'run on a different thread
if arraylist.count > 0
return arraylist(0)
end if
end function

hmmm, this is a syncronized collection, but the problem is that it is only
syncronized between calls to it's methods - so what happens if you get this
order of execution:

'arraylist starts with count 10
function acoolfunction
if arraylist.count > 0

---- Task switch

sub somesub
arraylist.clear()

--- Task Switch

return arraylist(0) ' BIG FAT EXCEPTION

See, in these kinds of operations, you really need something like an sql
transaction - to treat multiple operations as atomic. You achieve that using
the synclock statement (or some other locking mechanism, such as
system.threading.monitor). This is refered to as a "critical section". Then
the code would look something like:

sub somesub ' run on a thread
synclock arraylist.syncroot
arraylist.clear()
end synclock
end sub

function acoolfunction ' run on a different thread
synclock arraylist.syncroot ' lock the arraylist
if arraylist.count > 0
return arraylist(0)
endif
end synclock
end function

Now those blocks of code can not be entered at the same time... This makes
sure that the execution scenario from my example does not occur. All requests
will be queued, until the end of the synclock block... Make sense? Also, be
aware that the object you lock on needs to be the same for all the sync'd
operations and it must be a reference type. You can pass in a value, type and
you won't get a compiler error - you code just won't be thread safe. This is
because synclock expects the sync object to be of type object - if you pass in
say an integer value, then it will be boxed - but the object wrapper will be
different in each block of code.

I find most of the operations I do are like the above and involve multiple calls
to the collection, so I very seldom use the syncronized wrapper - that just
adds extra locking overhead for every call. So, I tend to use code similar to
the above, which does basically the same thing except over mutlipe calls.

Watch out for deadlocks, etc. Multi-threaded code is very hard to get right,
so be prepared for problems :) I have done quite a bit of multi-threaded code
over the last couple of years - and I still don't feel I'm anywhere near an
expert at it...
 
F

fniles

Thank you very much for your reply.

I have a few more questions.
1. In your example using the synclock statement, did you mean that you use
arraylist and not the syncronized arraylist with the synclock ?
2. >Also, be aware that the object you lock on needs to be the same for all
the sync'd operations and it must be a reference type.
In my program I did not pass in the parameter. The arraylist and its
syncronized arraylist are in the class like the following:
Private Quotes As New ArrayList
Private QuotesSync As ArrayList = ArrayList.Synchronized(Quotes)
3. Can I simply trap the exception when it happen like the following ?
sub somesub ' run on a thread
try
arraylist.clear ()
Catch ex As Exception
End Try
end sub

function acoolfunction 'run on a different thread
if arraylist.count > 0
try
return arraylist(0)
Catch ex As Exception
End Try
end if
end function

4. What can I do when deadlocks happens ?
 
T

Tom Shelton

Thank you very much for your reply.

I have a few more questions.
1. In your example using the synclock statement, did you mean that you use
arraylist and not the syncronized arraylist with the synclock ?

I generally don't use a sync'd arraylist. Simply because it is rare that I
use atomic operations, generally I want to do stuff like in my acoolfunction -
do a test and an operation. The sync'd arraylist won't do anything for you in
these cases - except add the extra locking overhead. So, I generally just use
the arraylist with it's syncroot property.
2. >Also, be aware that the object you lock on needs to be the same for all
the sync'd operations and it must be a reference type.
In my program I did not pass in the parameter. The arraylist and its
syncronized arraylist are in the class like the following:
Private Quotes As New ArrayList
Private QuotesSync As ArrayList = ArrayList.Synchronized(Quotes)

That's fine. My code was psuedo code. So, in your case:

SyncLock Me.Quotes.SyncRoot
' do cool stuff with quotes
End SyncLock

You just need to make sure that all the methods you are syncronizing are using
the same reference.
3. Can I simply trap the exception when it happen like the following ?
sub somesub ' run on a thread
try
arraylist.clear ()
Catch ex As Exception
End Try
end sub

function acoolfunction 'run on a different thread
if arraylist.count > 0
try
return arraylist(0)
Catch ex As Exception
End Try
end if
end function

Sure. You could trap the exception. The point of the example was to show
what might happen... You just need to be aware that in a multi-threaded
application you can not be sure of your order of execution - so in some cases
you need to introduce locking to ensure that bad things don't happen.
4. What can I do when deadlocks happens ?

Not a lot :) The thing to do with deadlocks is to make sure they don't
happen. There are lots of techniques on the web for doing so. One is using a
timed lock. There is a discussion of this technique at:

http://www.interact-sw.co.uk/iangblog/2004/03/23/locking

The code is C#, but this would apply in VB.NET as well.

There are other techniques for prevention and detection of deadlocks - but
that is a very advanced topic. One that I myself am still exploring. Like I
said, I'm far from an expert on this topic - even though I've done lots of it
over the last two to three years :)
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Top