PC Review


Reply
Thread Tools Rate Thread

C# Multi-Threading Lock Issue

 
 
=?Utf-8?B?R3JleUFsaWVuMDA3?=
Guest
Posts: n/a
 
      20th Sep 2005
I have a server application. This application has two pieces of shared data.
They are both hashtables. The untroubling hashtable holds the clients data.
The hashtable that is giving me grief represents a list of all of the
clients connected to the server.

Periodically I go through the client list (a.k.a, the irritating hashtable)
using an enumeration to make sure they are still active. The code looks like
this:

IDictionaryEnumerator i = ClientList.GetEnumerator();
while (i.MoveNext())
{
ClientKeepAliveHandler handler = null;
try
{
handler = (ClientKeepAliveHandler)((object[])i.Value)[1];
IAsyncResult ar = handler.BeginInvoke(new
AsyncCallback(this._asyncCallBack), i.Entry);
}
catch (System.Exception ex)
{
_writeError((int)i.Key,ref ex,MethodBase.GetCurrentMethod().Name);
}
}

Now, this iteration continues happliy along and as the asynchronous call
comes back the _asyncCallBack function gets called that looks like this:


DictionaryEntry clientData = (DictionaryEntry)ar.AsyncState;
ClientKeepAliveHandler del = null;

try
{

del = (ClientKeepAliveHandler)((object[])clientData.Value)[1];
del.EndInvoke(ar);
}
catch (Exception ex)
{
ClientKeepAlive -= del;
Exception e = new Exception("Client to be disconnected (most likely due to
inaccessibility).", ex);
_writeError((int)clientData.Key, ref e, MethodBase.GetCurrentMethod().Name,
"SessionId: " + clientData.Key.ToString() + ", Inner Message: " +
e.InnerException.Message);
CloseSession((int)(clientData.Key));
}

Now, if a client cannot be reached (as in un graceful shutdown) the
EndInvoke method raises an exception of "The remote machine doesn't like you"
or something like that. If that happens the code jumps to the error handler,
removes the delegate from the invokation list of the classes KeepAlive event
and sends an email to the developers who may have random bugs in there code.
Then the last line of the error handler closes the session and removes the
clients data. Keep in mind while this code is executing the original code
that started this may still be looping through the hashtable sending off
asynchrounous calls (and is whenever I experience the problem I'm about to
describe). That being said, when the CloseSession function removes an item
from the Hashtable the enumeration bombs with a "oh no the hashtable has
changed" error, as it should.

Now that I have explained all that the question: How should I implement
locking?
The behavior I want is the _asyncCallBack functions should all wait until I
finish looping before even being able to call EndInvoke. So I tried
implementing this in many ways. lock(ClientList), lock(ClientList.SyncRoot),
Hashtable SyncedHash = Hashtable.Synchronized(ClientList) with a
lock(SyncedHash), Monitor.Enter/Exit, lock(SomeRandomObjectForLocking) all
seem to not work. To be clear, I placed the lock statements right before
ClientKeepAlive -= del in the _asyncCallBack function and right before
IDictionaryEnumerator i = ClientList.GetEnumerator() in the original
snipped...help?
 
Reply With Quote
 
 
 
 
Richard Blewett [DevelopMentor]
Guest
Posts: n/a
 
      20th Sep 2005
Clone the dictionary in the method that checks keep-alive and modify the original list in the callback that says they're dead. You'll need to lock around the clone and modify

Regards

Richard Blewett - DevelopMentor
http://www.dotnetconsult.co.uk/weblog
http://www.dotnetconsult.co.uk

I have a server application. This application has two pieces of shared data.
They are both hashtables. The untroubling hashtable holds the clients data.
The hashtable that is giving me grief represents a list of all of the
clients connected to the server.

Periodically I go through the client list (a.k.a, the irritating hashtable)
using an enumeration to make sure they are still active. The code looks like
this:

IDictionaryEnumerator i = ClientList.GetEnumerator();
while (i.MoveNext())
{
ClientKeepAliveHandler handler = null;
try
{
handler = (ClientKeepAliveHandler)((object[])i.Value)[1];
IAsyncResult ar = handler.BeginInvoke(new
AsyncCallback(this._asyncCallBack), i.Entry);
}
catch (System.Exception ex)
{
_writeError((int)i.Key,ref ex,MethodBase.GetCurrentMethod().Name);
}
}

Now, this iteration continues happliy along and as the asynchronous call
comes back the _asyncCallBack function gets called that looks like this:


DictionaryEntry clientData = (DictionaryEntry)ar.AsyncState;
ClientKeepAliveHandler del = null;

try
{

del = (ClientKeepAliveHandler)((object[])clientData.Value)[1];
del.EndInvoke(ar);
}
catch (Exception ex)
{
ClientKeepAlive -= del;
Exception e = new Exception("Client to be disconnected (most likely due to
inaccessibility).", ex);
_writeError((int)clientData.Key, ref e, MethodBase.GetCurrentMethod().Name,
"SessionId: " + clientData.Key.ToString() + ", Inner Message: " +
e.InnerException.Message);
CloseSession((int)(clientData.Key));
}

Now, if a client cannot be reached (as in un graceful shutdown) the
EndInvoke method raises an exception of "The remote machine doesn't like you"
or something like that. If that happens the code jumps to the error handler,
removes the delegate from the invokation list of the classes KeepAlive event
and sends an email to the developers who may have random bugs in there code.
Then the last line of the error handler closes the session and removes the
clients data. Keep in mind while this code is executing the original code
that started this may still be looping through the hashtable sending off
asynchrounous calls (and is whenever I experience the problem I'm about to
describe). That being said, when the CloseSession function removes an item
from the Hashtable the enumeration bombs with a "oh no the hashtable has
changed" error, as it should.

Now that I have explained all that the question: How should I implement
locking?
The behavior I want is the _asyncCallBack functions should all wait until I
finish looping before even being able to call EndInvoke. So I tried
implementing this in many ways. lock(ClientList), lock(ClientList.SyncRoot),
Hashtable SyncedHash = Hashtable.Synchronized(ClientList) with a
lock(SyncedHash), Monitor.Enter/Exit, lock(SomeRandomObjectForLocking) all
seem to not work. To be clear, I placed the lock statements right before
ClientKeepAlive -= del in the _asyncCallBack function and right before
IDictionaryEnumerator i = ClientList.GetEnumerator() in the original
snipped...help?

[microsoft.public.dotnet.framework]
 
Reply With Quote
 
0to60
Guest
Posts: n/a
 
      20th Sep 2005

"GreyAlien007" <(E-Mail Removed)> wrote in message
news:F2451CAB-E331-4986-AE4A-(E-Mail Removed)...
>
> Now that I have explained all that the question: How should I implement
> locking?
> The behavior I want is the _asyncCallBack functions should all wait until

I
> finish looping before even being able to call EndInvoke. So I tried
> implementing this in many ways. lock(ClientList),

lock(ClientList.SyncRoot),
> Hashtable SyncedHash = Hashtable.Synchronized(ClientList) with a
> lock(SyncedHash), Monitor.Enter/Exit, lock(SomeRandomObjectForLocking) all
> seem to not work. To be clear, I placed the lock statements right before
> ClientKeepAlive -= del in the _asyncCallBack function and right before
> IDictionaryEnumerator i = ClientList.GetEnumerator() in the original
> snipped...help?


You are trying to remove one of the delegates in the callback. I'm assuming
this is going off in another thread while the first thread is still
iterating through the map. Are you getting a "the map was modified as you
were iterating through" message? It seems like you would.

The simplest way to do this type of thing is to iterate through a clone of
the map. So, you're iterating through a clone of the map, but removing
elements from the original map. You're not actually modifying the map
you're iterating through, know what I mean?

foreach(ClientKeepAliveHandler handler in ClientList.Values.Clone())
handler.BeginInvoke(...)



 
Reply With Quote
 
Jon Skeet [C# MVP]
Guest
Posts: n/a
 
      21st Sep 2005
GreyAlien007 <(E-Mail Removed)> wrote:
> I have a server application. This application has two pieces of shared data.
> They are both hashtables. The untroubling hashtable holds the clients data.
> The hashtable that is giving me grief represents a list of all of the
> clients connected to the server.
>
> Periodically I go through the client list (a.k.a, the irritating hashtable)
> using an enumeration to make sure they are still active. The code looks like
> this:
>
> IDictionaryEnumerator i = ClientList.GetEnumerator();
> while (i.MoveNext())
> {
> ClientKeepAliveHandler handler = null;
> try
> {
> handler = (ClientKeepAliveHandler)((object[])i.Value)[1];
> IAsyncResult ar = handler.BeginInvoke(new
> AsyncCallback(this._asyncCallBack), i.Entry);
> }
> catch (System.Exception ex)
> {
> _writeError((int)i.Key,ref ex,MethodBase.GetCurrentMethod().Name);
> }
> }


That seems quite a long way of doing it - why not use foreach?

Anyway, the problem is that you need to lock the table while you're
iterating through it and when you modify it. Either lock its SyncRoot
or on some other reference you can reach from all the places you need
it.

<snip>

> The behavior I want is the _asyncCallBack functions should all wait until I
> finish looping before even being able to call EndInvoke.


If you really want that, then you could lock the hashtable throughout
the use and before you call EndInvoke. However, that wouldn't solve
your problem - you could potentially find that the hashtable is being
looped through again before you get as far as closing the session.

The most elegant way which would involve the least locking would be the
way Richard suggested - take a "local" copy of the map (or in your
case, just the values) and then you can iterate through it without
worrying about locking while you're iterating. You would still need to
lock while copying the values out and for any modification, however.

--
Jon Skeet - <(E-Mail Removed)>
http://www.pobox.com/~skeet Blog: http://www.msmvps.com/jon.skeet
If replying to the group, please do not mail me too
 
Reply With Quote
 
 
 
Reply

Thread Tools
Rate This Thread
Rate This Thread:

Posting Rules
You may not post new threads
You may not post replies
You may not post attachments
You may not edit your posts

BB code is On
Smilies are On
[IMG] code is On
HTML code is Off
Trackbacks are On
Pingbacks are On
Refbacks are Off


Similar Threads
Thread Thread Starter Forum Replies Last Post
multi threading pls123 Microsoft Excel Programming 0 21st Sep 2008 10:33 PM
multi threading in C# =?Utf-8?B?Vmlua2k=?= Microsoft C# .NET 14 25th May 2007 07:50 PM
Correct Approach to Multi-Threading Issue =?Utf-8?B?Q29sbWVpc3Rlcg==?= Microsoft Dot NET Framework Forms 3 19th Apr 2005 12:02 PM
multi threading =?Utf-8?B?bm90cmVnaXN0ZXI=?= Microsoft VB .NET 13 8th Feb 2005 01:41 AM
Multi Threading VSK Microsoft ASP .NET 2 12th Mar 2004 02:54 PM


Features
 

Advertising
 

Newsgroups
 


All times are GMT +1. The time now is 10:42 AM.