using System; using System.Threading; class Database { public void SaveData(string text) { Console.WriteLine("Database.SaveData - Started"); Console.WriteLine("Database.SaveData - Working"); for (int i = 0; i < 100; i++) { Console.Write(text); } Console.WriteLine("\nDatabase.SaveData - Ended"); } } class ThreadMonitor1App { public static Database db = new Database(); public static void WorkerThreadMethod1() { Console.WriteLine("Worker thread #1 - Started"); Console.WriteLine ("Worker thread #1 -Calling Database.SaveData"); db.SaveData("x"); Console.WriteLine("Worker thread #1 - Returned from Output"); } public static void WorkerThreadMethod2() { Console.WriteLine("Worker thread #2 - Started"); Console.WriteLine ("Worker thread #2 - Calling Database.SaveData"); db.SaveData("o"); Console.WriteLine("Worker thread #2 - Returned from Output"); } public static void Main() { ThreadStart worker1 = new ThreadStart(WorkerThreadMethod1); ThreadStart worker2 = new ThreadStart(WorkerThreadMethod2); Console.WriteLine("Main - Creating worker threads"); Thread t1 = new Thread(worker1); Thread t2 = new Thread(worker2); t1.Start(); t2.Start(); } }
When you compile and execute this application, you'll see that the resulting output will have a mixture of o's and x's, showing that the Database.SaveData method is being run concurrently by both threads. (Note that once again I've abbreviated the output.) -
Main - Creating worker threads Worker thread #1 - Started Worker thread #2 - Started Worker thread #1 - Calling Database.SaveData Worker thread #2 - Calling Database.SaveData Database.SaveData - Started Database.SaveData - Started Database.SaveData - Working Database.SaveData - Working xoxoxoxoxoxoxoxoxoxoxoxoxoxoxoxoxoxoxoxoxoxoxox Database.SaveData - Ended Database.SaveData - Ended Worker thread #1 - Returned from Output Worker thread #2 - Returned from Output
Obviously, if the Database.SaveData method needed to finish updating multiple tables before being called by another thread, we'd have a serious problem.
To incorporate the Monitor class in this example, we use two of its static methods. The first method is called Enter. When executed, this method attempts to obtain a monitor lock on the object. If another thread already has the lock, the method will block until that lock has been released. Note that there is no implicit box operation performed here, so you can supply only a reference type to this method. The Monitor.Exit method is then called to release the lock. Here's the example rewritten to force the serialization of access to the Database.SaveData method: -
using System; using System.Threading; class Database { public void SaveData(string text) { Monitor.Enter(this); Console.WriteLine("Database.SaveData - Started"); Console.WriteLine("Database.SaveData - Working"); for (int i = 0; i < 100; i++) { Console.Write(text); } Console.WriteLine("\nDatabase.SaveData - Ended"); Monitor.Exit(this); } } class ThreadMonitor2App { public static Database db = new Database(); public static void WorkerThreadMethod1() { Console.WriteLine("Worker thread #1 - Started"); Console.WriteLine ("Worker thread #1 - Calling Database.SaveData"); db.SaveData("x"); Console.WriteLine("Worker thread #1 - Returned from Output"); } public static void WorkerThreadMethod2() { Console.WriteLine("Worker thread #2 - Started"); Console.WriteLine ("Worker thread #2 - Calling Database.SaveData"); db.SaveData("o"); Console.WriteLine("Worker thread #2 - Returned from Output"); } public static void Main() { ThreadStart worker1 = new ThreadStart(WorkerThreadMethod1); ThreadStart worker2 = new ThreadStart(WorkerThreadMethod2); Console.WriteLine("Main - Creating worker threads"); Thread t1 = new Thread(worker1); Thread t2 = new Thread(worker2); t1.Start(); t2.Start(); } }
Notice in the following output that even though the second thread called the Database.SaveData method, the Monitor.Enter method caused it to block until the first thread had released its lock: -
Main - Creating worker threads Worker thread #1 - Started Worker thread #2 - Started Worker thread #1 - Calling Database.SaveData Worker thread #2 - Calling Database.SaveData Database.SaveData - Started Database.SaveData - Working xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx Database.SaveData - Ended Database.SaveData - Started Worker thread #1 - Returned from Output Database.SaveData - Working ooooooooooooooooooooooooooooooooooooooo Database.SaveData - Ended Worker thread #2 - Returned from Output