Thread safety in Hashtables remove method ..

A

Anders Borum

Hello!

I'm am currently working on making a central cache component threadsafe, and
was looking at the synchronized implementation of the Hashtable. I was
wondering why you'd really want to enforce thread safety in a remove method?

Why would you care if two threads are trying to remove the same key from the
hashtable? In the end, the key is removed (and that was probably what you
were looking for).

Maybe this is caused by me not reading the following code correctly - and
not seing what it actually does is to make sure that no two threads are
removing keys, while other operations are performed on the hashtable (by
other threads).

public override void Remove(object key)
{
object local;

Monitor.Enter(local = _table.get_SyncRoot());
try
{
_table.Remove(key);
}
finally
{
Monitor.Exit(local);
}
}
 
J

Justin Rogers

Why would you care if two threads are trying to remove the same key from the
hashtable? In the end, the key is removed (and that was probably what you
were looking for).

The problem is that the Hashtable can be grown by adding items while items are
being removed. These growth operations change the way the Hashtable works
such that if the Remove method init's it's search values for finding the key,
and then
the underlying collection changes, you'll get erroneous results.

If the method were to take a local reference of buckets, then even stranger
results
would occur. When the collection was grown, the key would be removed from the
previous collection, and not the grown collection. In effect the item wouldn't
be
removed.
 
A

Anders Borum

Hello!

So the locking process occurs on the entire instance of the instance,
effectively serializing all write (or change operations), so that only on
thread is able to change the instance at any given time?

Naturally all threads are allowed read operations.

Considering the following two methods, I would like to know if the locking
occurs on the instance (_table.get_SyncRoot()) or on the local object
variable (named "local").

public override void Add(object key, object value)
{
object local;
Monitor.Enter(local = _table.get_SyncRoot());
try
{
_table.Add(key, value);
}
finally
{
Monitor.Exit(local);
}
}

public override void Remove(object key)
{
object local;
Monitor.Enter(local = _table.get_SyncRoot());
try
{
_table.Remove(key);
}
finally
{
Monitor.Exit(local);
}
}

... isn't the above equal to (given that the entire instance is locked):

public override void Remove(object key)
{
object local;
lock(local = _table.get_SyncRoot());
{
_table.Remove(key);
}
}

or simply this:

public override void Remove(object key)
{
lock(_table.get_SyncRoot());
{
_table.Remove(key);
}
}

Thanks in advance. I am really currently reading up on threading and would
like some pointers to emphasize what I get from the books. The more I read,
the more I understand just how important thread safety is to e.g.
collections that serve as caches ..
 
J

Jon Skeet [C# MVP]

Anders Borum said:
So the locking process occurs on the entire instance of the instance,
effectively serializing all write (or change operations), so that only on
thread is able to change the instance at any given time?
Yes.

Naturally all threads are allowed read operations.

That's not necessarily "natural" - if a writing thread temporarily has
the table in an invalid state, reading from it would be a bad idea.
Considering the following two methods, I would like to know if the locking
occurs on the instance (_table.get_SyncRoot()) or on the local object
variable (named "local").

They're the same thing. The code below is exactly equivalent to

lock (_table.SyncRoot)
{
...
}

as you suggested (using the property syntax rather than calling the
get_SyncRoot method directly).
Thanks in advance. I am really currently reading up on threading and would
like some pointers to emphasize what I get from the books. The more I read,
the more I understand just how important thread safety is to e.g.
collections that serve as caches ..

See http://www.pobox.com/~skeet/csharp/threads/ for most of what I know
about threading.
 
A

Anders Borum

Hello!
That's not necessarily "natural" - if a writing thread temporarily has
the table in an invalid state, reading from it would be a bad idea.

Sorry; What I ment was, that from what I have seen in the Hashtable, reading
operations are not synchronized. Again, this is because the implementation
of the Hashtable allows for this. I agree with you that one could easily
think of situations where all operations on a instance are synchronized.
as you suggested (using the property syntax rather than calling the
get_SyncRoot method directly).

The code was taken directly from Salamanders C# decompiler. I am using the
SyncRoot property in my own code. Also thanks for clearing up the
lock(this); code.
See http://www.pobox.com/~skeet/csharp/threads/ for most of what I know
about threading.

I'll read that right away.
 

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