views:

81

answers:

1

I have a class (simplified example) like :

public class SomeCollection : ICloneable
{
    public void Add(Item item) { /* ... */ }
    public void Remove(Item item) { /* ... */ }
    public Item Get(Key key) { /* ... */ }
    /*
    ...
    */
    public object Clone() { /* ... */ }
}

I need that when a thread enters Clone() no other thread can enter in Add or Remove but can enter in Get. At first I thought of :

    public void Add(Item item) { lock(addLock) { /* ... */ } }
    public void Remove(Item item) { lock(removeLock) { /* ... */ } }

    public object Clone(Item item)
    { 
        lock(addLock)
        {
            lock(removeLock)
            {
                /* ... */
            }
        }
    }

This works (I think) but has certain disadvantages : * I don't want two threads entering Add to block one another - I am dealing with that deeper in code * I will have to withstand the locking overhead for each call to Add or Remove

Then I thought of this

    private volatile bool cloning = false; // notice the volatile keyword

    public void Add(Item item)
    {
         int i = 0;
         while(cloning)
         { 
             if (i++ > 20)
                 throw new TimeoutException();
             Thread.Sleep(50); // waits 50 milliseconds
         }
         /* ... */
    } // and the same for Remove

    public object Clone()
    {
        cloning = true;
        try
        {
            /* do the cloning */
        } finally { cloning = false; }
    }

However this approach :

  • Is more complex
  • Clone can enter while a thread hasn't finished executing Add or Remove
  • Seems unnatural

I have given ReadWriterLockSlim a short glance but doesn't seem to fit in my scenario.

I need this because the Clone method takes long time (may take over a second - the collection is HUGE) and an alteration in this time will blow up the enumerator (used in a foreach loop). Yes, I must use the foreach since the underlying key collection does not expose anything else than an IEnumerable.

What wouly you recommend?

+1  A: 

Could you explain more why ReaderWriterLockSlim doesn't fit for your scenario?

Sure, you won't use it for it's classic use, but consider the Clone method as your writer and the add/remove methods as your readers and I think it fits.

[edit] One other thing: If you go this route, make sure you document why you're using ReaderWriterLockSlim 'backwards' so the next person who reads the code (or you in six months) understands what's going on.

Jonathan
Yes, I considered the same thing : Clone - the writer, Add and Remove as readers. One, the operation names are inverted I mean Add and Remove are really doing writings and Clone does a reading. But this is more like a non-issue. Second I don't really understand for sure if it will block a read operation while in a write and block a write operation while in a read. But again, I should read again the documentation. I surely hope you are right about it. It would answer my question perfectly. :D TX!
Andrei Rinea
I have read the documentation and it has a nice table which explains clearly the possible interactions between a thread and the lock. Thank you. This seems to be the answer for me.
Andrei Rinea
@Jared - sure, in the normal usage, but not the way the OP described it -- he wants multiple add/removes to be concurrent and only clones to block the read/writes.
Jonathan