After I read this question with an answer from Marc....
I sometimes see people locking on a local variable.
Is this code broken?
You are creating the o object every time your method is called. So, lock won't work. I mean other threads won't wait for the lock to be non signaled and grab control over a resource which this lock controls. Usually a lock objects is a private variable in a class so that all methods look into the same object.
Yes. It is broken.
You want a static readonly object as a private field to lock on. As you suspect, your example code creates a new object every time you call Do, and hence the lock will have nothing to hold onto and won't work at all.
private static object syncRoot = new object();
lock (syncRoot) { }
I believe
object o = new Object();
should be outside the method as aField
.Since each thread is getting a new instance of
o
, there will be multiple locks.What am I missing here? Shouldn't it lock on fields in this specific case?
Your understanding is correct. The code is broken. In this implementation even though lock will be active, it will not provide synchronization as it will be on different objects.
From Microsoft Docs
When you synchronize thread access to a shared resource, lock on a dedicated object instance (for example, private readonly object balanceLock = new object();) or another instance that is unlikely to be used as a lock object by unrelated parts of the code. Avoid using the same lock object instance for different shared resources, as it might result in deadlock or lock contention. In particular, avoid using the following as lock objects:
this, as it might be used by the callers as a lock. Type instances, as those might be obtained by the typeof operator or reflection. string instances, including string literals, as those might be interned. Hold a lock for as short time as possible to reduce lock contention.
Locking on local variable, the lock won's work. Locking on global variable can take effect to synchronize multiple thread.
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Threading;
namespace testLock
{
class Program
{
public static void Main()
{
// Start a thread that calls a parameterized static method.
for(int i = 0; i< 10;i++)
{
Thread newThread = new Thread(DoWork);
newThread.Start(i);
}
Console.ReadLine();
}
static object gObject= new object();
public static void DoWork(object data)
{
int len = (int)data % 3;
object tmp = new object();
Console.WriteLine("to lock...... Data='{0}' sleepTime:{1}", data, len);
lock (tmp)//tmp won't work, change tmp to gObject to see different output, which is good locking case)
{
Console.WriteLine("in lock...... Data='{0}' sleepTime:{1}", data, len);
Thread.Sleep( len* 1000);
Console.WriteLine("Static thread procedure. Data='{0}' sleepTime:{1}", data, len);
}
}
}
}
**Lock temp variable,will output:**
to lock...... Data='1' sleepTime:1
in lock...... Data='1' sleepTime:1
to lock...... Data='2' sleepTime:2
in lock...... Data='2' sleepTime:2
to lock...... Data='0' sleepTime:0
in lock...... Data='0' sleepTime:0
Static thread procedure. Data='0' sleepTime:0
to lock...... Data='3' sleepTime:0
in lock...... Data='3' sleepTime:0
Static thread procedure. Data='3' sleepTime:0
to lock...... Data='4' sleepTime:1
in lock...... Data='4' sleepTime:1
to lock...... Data='5' sleepTime:2
in lock...... Data='5' sleepTime:2
to lock...... Data='6' sleepTime:0
in lock...... Data='6' sleepTime:0
Static thread procedure. Data='6' sleepTime:0
to lock...... Data='7' sleepTime:1
in lock...... Data='7' sleepTime:1
to lock...... Data='8' sleepTime:2
in lock...... Data='8' sleepTime:2
to lock...... Data='9' sleepTime:0
in lock...... Data='9' sleepTime:0
Static thread procedure. Data='9' sleepTime:0
Static thread procedure. Data='1' sleepTime:1
Static thread procedure. Data='4' sleepTime:1
Static thread procedure. Data='7' sleepTime:1
Static thread procedure. Data='2' sleepTime:2
Static thread procedure. Data='5' sleepTime:2
Static thread procedure. Data='8' sleepTime:2
**Then lock gObject, will print:**
to lock...... Data='0' sleepTime:0
in lock...... Data='0' sleepTime:0
to lock...... Data='1' sleepTime:1
to lock...... Data='2' sleepTime:2
Static thread procedure. Data='0' sleepTime:0
in lock...... Data='1' sleepTime:1
to lock...... Data='3' sleepTime:0
to lock...... Data='4' sleepTime:1
to lock...... Data='5' sleepTime:2
to lock...... Data='6' sleepTime:0
to lock...... Data='7' sleepTime:1
to lock...... Data='8' sleepTime:2
to lock...... Data='9' sleepTime:0
Static thread procedure. Data='1' sleepTime:1
in lock...... Data='5' sleepTime:2
Static thread procedure. Data='5' sleepTime:2
in lock...... Data='9' sleepTime:0
Static thread procedure. Data='9' sleepTime:0
in lock...... Data='2' sleepTime:2
Static thread procedure. Data='2' sleepTime:2
in lock...... Data='8' sleepTime:2
Static thread procedure. Data='8' sleepTime:2
in lock...... Data='7' sleepTime:1
Static thread procedure. Data='7' sleepTime:1
in lock...... Data='4' sleepTime:1
Static thread procedure. Data='4' sleepTime:1
in lock...... Data='3' sleepTime:0
Static thread procedure. Data='3' sleepTime:0
in lock...... Data='6' sleepTime:0
Static thread procedure. Data='6' sleepTime:0
I, personally, don't see any reason of using this, as lock
just sets the special field in the instance of the o
, to signaled state. So other threads can check the state of that instance, and based on that execute the code inside the lock
statement or wait release of it.
Having local variable every time will allocate a new instance, so for every thread it will be ok.
Don't see any meaning in this.