views:

85

answers:

3

I have a grid which is defined as: List<List<Cell>>, where "Cell" is a custom class of mine. My program has several threads which access various coordinates on the grid, and change data in the "Cell" class. But I only want one thread writing data to a "Cell" object at a time. I thought using concurrent collections such as ConcurrentBag would be of service, but it seems that all the Concurrent Collections only have methods to ADD items or REMOVE them from the collection. There doesn't seem to be a thread-safe way to CHANGE data held within such a collection.

Am I missing something here, or is there no "easy way" to do it using such collections?

A: 

You can simply use mutexes for thread-safe access to your cell contents. It works something like this:

class Cell{

 private static Mutex mut = new Mutex();

    private static void SetResource(...)
    {
        // Wait until it is safe to enter.
        mut.WaitOne();

        //change cell contents here...

        // Release the Mutex.
        mut.ReleaseMutex();
    }

   }

See http://msdn.microsoft.com/en-us/library/system.threading.mutex.aspx for more details.

Adrian Grigore
+1  A: 

Have you read this paper?

Thread-safe Collections in .NET Framework 4 and Their Performance Characteristics

Sample code using ConcurrentBag:

if (bag.TryTake(out node))
{
    for (int i = 0; i < node.Children.Count; i++)
    {
        bag.Add(node.Children[i]);
    }

    ProcessNode(node); //e.g. a short string comparison
}

You can also take a look at ConcurrentDictionary’s support for adding and updating.

Leniel Macaferi
A: 

The concurrent collections were intended as a means of safely modifying the collection itself; not the items. You will have to add your own synchronization primitives to the Cell class. If the outer and inner List instances themselves will remain unchanged there is no need to apply synchronization to them.

You mentioned that you will have many readers, but only one writer. That is an incredibly important detail. It means that any lock-free synchronization strategies become dramatically easier to implement. However, I do not encourage going down that route as it would still be quite difficult to get right. But, it could also mean that the ReaderWriterLockSlim might perform better than a lock.

You will have to experiment with both to see which one provides the best balance of maintainability and efficiency. My hunch is that you will find a traditional lock will perform faster despite having multiple readers and a single writer, but it is worth testing. It is certainly a lot easier on the fingers when typing out the code.

Here is sample code for both.

public class Cell
{
  private object m_LockObject = new object();

  public object ReadMyState()
  {
    lock (m_LockObject)
    {
      // Return the data here.
    }    
  }

  public void ChangeMyState()
  {
    lock (m_LockObject)
    {
      // Make your changes here.
    }    
  }
}

and

public class Cell
{
  private ReaderWriterLockSlim m_LockObject = new ReaderWriterLockSlim();

  public object ReadMyState()
  {
    m_LockObject.EnterReadLock();
    try
    {
      // Return the data here.
    }
    finally
    {
      m_LockObject.ExitReadLock();
    }
  }

  public void ChangeMyState()
  {
    m_LockObject.EnterWriteLock();
    try
    {
      // Make your changes here.
    }
    finally
    {
      m_LockObject.ExitWriteLock();
    }
  }
}
Brian Gideon