Recently we had a problem with a multi-threaded app. The symptoms were database locks.
In order to solve it a co-worker (who was not at his best, to say the least) did something like this:
lock(this)
{
//Some actions
}
looks normal? Well, not if the class was instantiated on the thread. In those cases this lock is utterly useless. Here's a simple code to demonstrate the issue.
using System;
namespace lock_test
{
internal class MyClass
{
private string ID;
public MyClass(string inID)
{
ID = inID;
}
public void DoRun()
{
lock (this)
{
Console.WriteLine("Start " + ID);
System.Threading.Thread.Sleep(10000);
Console.WriteLine("Stop " + ID);
}
}
}
class Program
{
static void Main(string[] args)
{
for (int i = 0; i < 5; i++)
{
MyClass x = new MyClass(i.ToString());
System.Threading.Thread th = new System.Threading.Thread(x.DoRun);
th.Start();
System.Threading.Thread.Sleep(100);
}
System.Console.ReadKey();
}
}
}
This code produces the result
Start 0
Start 1
Start 2
Start 3
Start 4
And a few seconds afterwards adds
Stop 0
Stop 1
Stop 2
Stop 3
Stop 4
The "lock(this)" statement was completely ignored, for practical purposes, putting a timer on it might show the compiler didn't remove it.
So how should it be done?
1. Add to the internal class a new static variable
private static object lock_obj = new object();
2. Change the "lock(this)" to "lock(lock_obj)".
That's all, suddenly the app works as expected, the output (in my last run) was:
Start 0
Stop 0
Start 3
Stop 3
Start 2
Stop 2
Start 1
Stop 1
Start 4
Stop 4
Note the order, it's due to the lock. The 1st thread (ID 0) locked it, but afterwards it's up to the mutex handling mechanism to decide who does the new lock. The only thing guaranteed is you'll see the "Start" and the "Stop" of the same ID, unlike the output of the first run.
Subscribe to:
Post Comments (Atom)
No comments:
Post a Comment