C# lock statement
-
Hi buddies, I have discovered something so interesting to myself! Wana share this top secret discovery! here: We all know that 'lock(lockObject) {...}' causes no more than one thread can enter the block of code (Critical section). I always thought about the argument 'lockObject' that was usually 'this' and its usage while it seems the lock functionality doesn't care about what that argument is. Recently I concluded the argument is so so important and it has a major usage and that is it lets us create critical sections that are not just one single code block, and different code blocks in different methods even in different classes may construct ONE critical section that no more than one thread may exist in the whole section. I mean we can group various code blocks into unique critical section by assigning a single object reference as their lock(lockObject) argument. And also one great result is that we can create different critical sections in one class by assigning different lock arguments instead of 'this'; and these sections are independent. So all you already knew this?! ok but atleast please agree that it is not well documented in MSDN or perhaps it is, but I have never seen it explicitly in the sections discussing lock statement. Thanks for your note, - Mohammad My test console application code: using System; using System.Threading; namespace TestConsole { /// /// Summary description for Class1. /// class Class1 { private static readonly object _lockObject1 = new object(); private static readonly object _lockObject2 = new object(); /// /// The main entry point for the application. /// [STAThread] static void Main(string[] args) { Thread t1 = new Thread(new ThreadStart(Method1)); Thread t2 = new Thread(new ThreadStart(Method2)); Thread t3 = new Thread(new ThreadStart(Method3)); t1.Start(); t2.Start(); t3.Start(); Console.ReadLine(); } static public void Method1() { lock(_lockObject1) { // critical section 1 for(int i = 0; i < 20; i++) { Console.WriteLine("Thread:1 " + i); Thread.Sleep(100); } } } static public void Method2() { lock(_loc
-
Hi buddies, I have discovered something so interesting to myself! Wana share this top secret discovery! here: We all know that 'lock(lockObject) {...}' causes no more than one thread can enter the block of code (Critical section). I always thought about the argument 'lockObject' that was usually 'this' and its usage while it seems the lock functionality doesn't care about what that argument is. Recently I concluded the argument is so so important and it has a major usage and that is it lets us create critical sections that are not just one single code block, and different code blocks in different methods even in different classes may construct ONE critical section that no more than one thread may exist in the whole section. I mean we can group various code blocks into unique critical section by assigning a single object reference as their lock(lockObject) argument. And also one great result is that we can create different critical sections in one class by assigning different lock arguments instead of 'this'; and these sections are independent. So all you already knew this?! ok but atleast please agree that it is not well documented in MSDN or perhaps it is, but I have never seen it explicitly in the sections discussing lock statement. Thanks for your note, - Mohammad My test console application code: using System; using System.Threading; namespace TestConsole { /// /// Summary description for Class1. /// class Class1 { private static readonly object _lockObject1 = new object(); private static readonly object _lockObject2 = new object(); /// /// The main entry point for the application. /// [STAThread] static void Main(string[] args) { Thread t1 = new Thread(new ThreadStart(Method1)); Thread t2 = new Thread(new ThreadStart(Method2)); Thread t3 = new Thread(new ThreadStart(Method3)); t1.Start(); t2.Start(); t3.Start(); Console.ReadLine(); } static public void Method1() { lock(_lockObject1) { // critical section 1 for(int i = 0; i < 20; i++) { Console.WriteLine("Thread:1 " + i); Thread.Sleep(100); } } } static public void Method2() { lock(_loc
Actually, it's recommended you never use lock(this) [that is, locking the class instance] or lock(typeof(TestConsole)) [that is, locking the class type itself). It is highly recommended that if you do use locks, then you should lock private variables in your class, rather than class instance or the class type. The reason is that by using private variables to do locking, you have knowledge of and can control everyone that holds locks on your object. This would not necessarily be true for locking a class instance or the class itself. Also, as an FYI, the C# lock statement is just syntax candy:
lock(_lockObj)
{
Console.WriteLine("hello!");
}will get compiled down to:
System.Threading.Monitor.Enter(_lockObj);
try
{
Console.WriteLine("hello");
}
finally
{
System.Threading.Monitor.Exit(_lockObj);
}Tech, life, family, faith: Give me a visit. I'm currently blogging about: Morality Apart from God Judah Himango
-- modified at 11:20 Wednesday 9th November, 2005
-
Actually, it's recommended you never use lock(this) [that is, locking the class instance] or lock(typeof(TestConsole)) [that is, locking the class type itself). It is highly recommended that if you do use locks, then you should lock private variables in your class, rather than class instance or the class type. The reason is that by using private variables to do locking, you have knowledge of and can control everyone that holds locks on your object. This would not necessarily be true for locking a class instance or the class itself. Also, as an FYI, the C# lock statement is just syntax candy:
lock(_lockObj)
{
Console.WriteLine("hello!");
}will get compiled down to:
System.Threading.Monitor.Enter(_lockObj);
try
{
Console.WriteLine("hello");
}
finally
{
System.Threading.Monitor.Exit(_lockObj);
}Tech, life, family, faith: Give me a visit. I'm currently blogging about: Morality Apart from God Judah Himango
-- modified at 11:20 Wednesday 9th November, 2005
I agree with Judah, locking 'this' or the class itself is very bad practice, and leads to a lot of problems. Standard practice is to use a semaphore, or a synchronization root object, as a lock target. Some simple examples of good locking can be represented by a synchronized collection, or the singleton object: A singleton:
public class MySingleton
{
#region Singleton Pattern
private static volatile MySingleton m_instance;
private static object m_syncRoot = new object(); // lock targetpublic static MySingleton
{
get
{
if (m_instance == null)
{
// Lock the syncroot object, rather than typeof(MySingleton)
lock (m_syncRoot)
{
if (m_instance == null)
{
m_instance = new MySingleton();
}
}
}return m\_instance; }
}
#endregion
}Synchronizing a collection:
public class MyCollection: CollectionBase
{
private class MySyncCollection: MyCollection
{
internal MySyncCollection(MyCollection col)
{
m_col = col;
m_root = col.SyncRoot;
}private MyCollection m\_col; private object m\_root; public override object SyncRoot { get { return m\_root; } } public int Add(object item) { int index; lock (m\_root) { index = m\_col.Add(item); } return index; } // ... Override other methods ...
}
private object m_syncRoot = new object(); // lock target
public override object SyncRoot
{
get
{
m_syncRoot;
}
}public int Add(object item)
{
return List.Add(item);
}public static MyCollection Synchronized(MyCollection col)
{
if (col == null)
throw new ArgumentNullException("The specified collection is null. Can not synchronize a non-existent collection.");return new MyCollection.MySyncCollection(col);
}
// ... Other methods ...
} -
I agree with Judah, locking 'this' or the class itself is very bad practice, and leads to a lot of problems. Standard practice is to use a semaphore, or a synchronization root object, as a lock target. Some simple examples of good locking can be represented by a synchronized collection, or the singleton object: A singleton:
public class MySingleton
{
#region Singleton Pattern
private static volatile MySingleton m_instance;
private static object m_syncRoot = new object(); // lock targetpublic static MySingleton
{
get
{
if (m_instance == null)
{
// Lock the syncroot object, rather than typeof(MySingleton)
lock (m_syncRoot)
{
if (m_instance == null)
{
m_instance = new MySingleton();
}
}
}return m\_instance; }
}
#endregion
}Synchronizing a collection:
public class MyCollection: CollectionBase
{
private class MySyncCollection: MyCollection
{
internal MySyncCollection(MyCollection col)
{
m_col = col;
m_root = col.SyncRoot;
}private MyCollection m\_col; private object m\_root; public override object SyncRoot { get { return m\_root; } } public int Add(object item) { int index; lock (m\_root) { index = m\_col.Add(item); } return index; } // ... Override other methods ...
}
private object m_syncRoot = new object(); // lock target
public override object SyncRoot
{
get
{
m_syncRoot;
}
}public int Add(object item)
{
return List.Add(item);
}public static MyCollection Synchronized(MyCollection col)
{
if (col == null)
throw new ArgumentNullException("The specified collection is null. Can not synchronize a non-existent collection.");return new MyCollection.MySyncCollection(col);
}
// ... Other methods ...
}Thank you for your replies friends, While I am completely agree with your notes, I should say the concept I talked in my post was not a question about using or not using 'this' as the lock object. That was something interesting that I didn't know up to know and that is grouped locks (with a single lock object) togather that construct composite single critical sections, and creating INDEPENDENT critical sections by assigning different lock objects. I am excited about this because I didn't knew this before and non of my friends, thought so about lock usage. I posted here to see if anyone else see this interesting and usefull. --- "Art happens when you least expect it."