Thread Synch and Monitor: How to handle Value Types, and Private Locking to Avoid Deadlocks

I've been doing some reading on the Monitor class for thread synchronization.  Four questions have come up.

Here's a pretty good example from the MSoft docs of synchronizing access by using Monitor around a critical area of code:

class MonitorSample
{
 //Define the queue to safe thread access.
 private Queue m_inputQueue;
< xml:namespace prefix = o ns = "urn:schemas-microsoft-com:office:office" />  public MonitorSample()
 {
  m_inputQueue = new Queue(); 
 }
 //Add an element to the queue and obtain the monitor lock for the queue object.
 public void AddElement(object qValue)
 {
   //Lock the queue.
   Monitor.Enter(m_inputQueue);
  //Add element
   m_inputQueue.Enqueue(qValue);
   //Unlock the queue.
   Monitor.Exit(m_inputQueue);
 }
 
 static void < xml:namespace prefix = st1 ns = "urn:schemas-microsoft-com:office:smarttags" />Main(string[] args)
  {
    MonitorSample sample = new MonitorSample();
                       
   for(int i = 0; i < 30; i++)
       sample.AddElement(i);
 
... Etc.
I *think* the idea here is to synchronize access by 
locking on a private class level object, and then wrapping
that object around code in methods that would get or set 
values or run other tasks that require synchronization. 
From the docs, my understanding is that this is better than 
synchronizing on an entire public method as it cuts down on 
deadlocks, etc. 
From the above, the entry private Queue m_inputQueue; 
appears to be the private class level object in MonitorSample 
that's being used for this purpose. I *think* m_inputQueue is 
instantiated via the MonitorSample constructor.
 
Question 1: Is my understanding of the above correct 
Question 2: Assuming I'm close, is the fact that the private 
class object m_inputQueue happens to be the name of the 
actual queue in use to solve the business problem a 
coincidence, or is this necessary  In other words, If I wanted 
to could a set up a private class object named mySynchLock, 
instantiate it in the constructor, and wrap this around any 
critical code sections using Monitor.Enter and Monitor.Exit 
Question 3:  From the docs it says that you can use Monitor
to synchronize access to reference types, but you cannot use
it to synchronize access to value types due to boxing problems. 
I *believe* in the case above the private m_inputQueue is
a reference type. Would the following work to synchronize
access to an int value type field named myTotalCount:
  //Lock the queue.
  Monitor.Enter(m_inputQueue);
  //Add element
  m_inputQueue.Enqueue(qValue);
 // Increment the value field
 myTotalCount = myTotalCount + 1;
  //Unlock the queue.
  Monitor.Exit(m_inputQueue);
Or is there a better approach 
Question 4: All of the above is based on an 
instantiated class object MonitorSample with 
instance methods and instance fields. If I 
simply have a non-instantiated class that contains 
static methods and fields (such as a AddToCounter method,
a myCounter value field, etc.) what's the recommended 
approach to synchronize access to these 
 
Thanks as always for helping the new guy!
Doug
 



Answer this question

Thread Synch and Monitor: How to handle Value Types, and Private Locking to Avoid Deadlocks

  • JamesinSC

    Wow!  Great answers!  May I please ask you a couple of follow-on questions so that I really understand   Thanks again for helping a new guy!!!

    1:  You perform the locking with the Monitor class on the object you want exclusive rights ... 

    Is the idea that you want to lock an object's member that contains data you want to protect (like m_inputQueue) or do you lock an object's member that contains data in order to actually synchronize access to the execution of an object's method, such as all the code in AddMethod   What's the proper way to look at it:  protect the individua data member, or protect the access to the method (that in turn protects modifications to the data member)  

    2:  As with the above, if the private synchronization object is at the class level, are you actually locking just the private data member (and any method it is in), or are you actually synchronizing all access to the entire class, including all methods   As you say above, I assume the idea is to minimize the scope and duration of the synchronization object so I assume it is the former, but I want to make sure.

    3:  Are you saying you must use the Interlocked tools to lock a value reference    I was looking at the cast example, but was not able to determine if this could be used with Monitor.  The Interlocked tools look very ugly.

    4.  Finally, to synchronize access to static members, are you saying that I must instantiate an object instance to use Monitor   I was thinking of setting up a couple of classes that would not be instantiated as objects, but might have methods used to perform simple tasks, such as return a connection string, etc.  Does Monitor require and instantiated object   I think the answer is yes, and if so I'll work this out. 

    Thank you so much!!!

    Doug

     

     

     

     



  • DGardner

    You should protect the parts that actually read or modify shared data. The data is usually in an object or a value type but could also be some kind of external resource. If you do not lock the access any other thread might change it.

    In the example the shared data is the m_inputQueue so that is the object you need to synchronize access to. You do not need to synchronize the actual methods or the class itself.

    public void Add(Object item)
    {
       Monitor.Enter(m_inputQueue);
       m_inputQueue.Enqueue(item):
       Monitor.Exit(m_inputQueue);
    }
    public Object Get()
    {
       Object item;
       Monitor.Enter(m_inputQueue);
       item = m_inputQueue.Dequeue():
       Monitor.Exit(m_inputQueue);
       return item;
    }

    If a thread is adding an item any other thread trying to Add() or Get() will wait until the locking thread is finished with m_inputQueue.

    However you might have a method that does nothing with the shared data it will not be locked from usage by any thread.

    public Int32 GetLargest(Int32 nValue1, Int32 nValue2)
    {
       if (nValue1 > nValue2)
       {
          return nValue1;
       }
       else
       {
          return nValue2;
       }
    }

    If a thread is locking the m_inputQueue any other thread calling GetLargest() will not be not wait for the locking thread to release m_inputQueue.

    You do not have to use the Interlocked class for value types. You can use the Monitor class if you make sure you use the same object to lock on when reading/modifying the value type.  With same object I mean what object you pass to the Enter() and Exit() methods.

    You are right, the object must be instantiated to be able to use it for Monitor.Enter().

    static Queue m_inputQueue = new Queue();

    m_inputQueue will be instantiated as soon the class it is declared in is accessed. You can then use it to for locking access to it. If you work with strings or other immutable objects you need to take into consideration that modifying it will not stay the same object but it will be a new object.

    In that case it could be good to work with a singleton class instead. It is instatiated but there can only exist one instance of it. It has a private constructor and is instantiated by a static member.

    http://msdn.microsoft.com/library/default.asp url=/library/en-us/dnbda/html/singletondespatt.asp



  • Frederic13

    More great information from everyone!

    So I need to ensure I use the try, catch and finally construct any time I use Monitor.  OK, sounds good.

    Thanks again for everyone's input!  Any additional thoughts are always welcome!

     

    DB



  • Apoorva

    Hi,

    Is it ok to mix use of Monitor and lock in the following fashion or would it
    be better to use one or the other throughout


    protected int ActiveResyncThreads
    {
    get
    {
    lock (objLock)
    {
    return _ActiveResyncThreads;
    }
    }
    set
    {
    Monitor.Enter(objLock);
    _ActiveResyncThreads = value;
    Monitor.Exit(objLock);
    }
    }

    Many thanks

    Jay-r


  • Charlie C

    You perform the locking with the Monitor class on the object you want exclusive rights to, no other thread can acquire a lock at the same time.

    The m_inputQueue is instantiated in the constructor.

    m_inputQueue is just a name for the business logic. You could use a dummy sync object but it has to be instantiated as it can not be a null reference. There is usually no reason to spend time and memory on dummy objects. In an instantiated class you could use the this reference.

    You have the Interlocked class to modify value types. If it is not enough you store the reference to the boxed value type in the class and lock on that. You can box it by casting it to Object.
    Int32 n = 2;
    Object o = (Object)n;

    As long as you lock the object you are modifying the code is the same for static and instantiated classes and methods to code thread safe.

    Keep critical sections small and try to keep the number of places to a minimum.



  • Mehmet Metin Altuntas

    Is there any reason you prefer:

    Monitor.Enter(xxx); code....; Monitor.Exit(xxx)

    over

    lock(xxx) { code... }

    In the former, if code throws an exception, you will be in trouble, whereas the latter xxx will always be unlocked.


  • Omns

    I do not prefer Monitor.Enter()/Exit() over lock() in C#. Most of the thread sensitive classes I have written was in managed C++ so I am quite used to Monitor class.

    lock() makes it easier to write properly error handled code so it is to prefer.

    It should be something like this with properly error handled code using Monitor class.

    Monitor.Enter(...);
    try
    {
       ...
    }
    catch
    {
       ...
    }
    finally
    {
       Monitor.Exit(...);
    }

    Monitor class offers some more functionality than just Enter/Exit.



  • RomanJB

    Thank you again...this is very helpful!

     

    Doug



  • Thread Synch and Monitor: How to handle Value Types, and Private Locking to Avoid Deadlocks