0

My application was having some issue and I found out "lock" was causing the problem. I have simplified the issue into smaller code, could be more smaller. But this will do.

 public partial class App
    {
        protected override void OnStartup(StartupEventArgs e)
        {
            base.OnStartup(e);
            longProcessTimer = new System.Timers.Timer(3000);
            longProcessTimer.Elapsed += LongProcessElapsed;
            longProcessTimer.Start();
            SomeClass.Current.Start();
        }
        System.Timers.Timer longProcessTimer;
        static string LockingString = "TestString";
        private void LongProcessElapsed(object sender, EventArgs e)
        {
            Debug.WriteLine("Long Process Started");
            lock (LockingString)//this lock will block the sublock
            {
                longProcessTimer.Stop();

                Thread.Sleep(10000);//just for demo

                longProcessTimer.Start();
            }
            Debug.WriteLine("Long Process Ended");
        }
    }

    public class SomeClass
    {
        public static readonly SomeClass Current = new SomeClass();
        System.Timers.Timer SubTimer;
        Stopwatch stopWatch = new Stopwatch();

        protected string SubLockingString = "TestString";
        public SomeClass()
        {
            SubTimer = new System.Timers.Timer(500);
            SubTimer.Elapsed += SubTimerElapsed;
        }
        public void Start()
        {
            stopWatch.Start();
            SubTimer.Start();
        }
        private void SubTimerElapsed(object sender, EventArgs e)
        {
            SubTimer.Stop();
            stopWatch.Restart();
            lock (SubLockingString)
            {
                Debug.WriteLine("Inside sub lock, " + stopWatch.Elapsed);
            }
            SubTimer.Start();
        }
    }

Now if LockingString is locked, it will also lock SubLockingString. But if I change the value of any string, the locks wont interfere with each other.

I know the solution, but I'm just curious why is it happening ?

J. Doe
  • 157
  • 1
  • 1
  • 10
  • 1
    [because it's the same object (same reference)](https://dotnetfiddle.net/cYs6N1) ... instead string use object (`new object()`) – Selvin Mar 15 '19 at 11:44
  • 1
    _"Locking on strings is discouraged, the main reason is that (because of string-interning) some other code could lock on the same string instance without you knowing this"_ - https://stackoverflow.com/a/12804905/43846 – stuartd Mar 15 '19 at 11:45
  • 1
    You discovered that string literals are *interned*. In other words, the two "TestString" literals are in fact the same object. Nice demonstration why it is such a bad idea to use an arbitrary object for locking btw, use `new object()` instead. – Hans Passant Mar 15 '19 at 11:50

2 Answers2

1

You've fallen foul of String interning.

When the compiler sees two string literals which have the same value but are in different places, it turns them both into a single string object. Therefore your LockingString and SubLockingString point to the same object - if you run ReferenceEquals(LockingString, SubLockingString) you'll get back true, which shows that these are both references to the same underlying string.

You should only ever lock on a dedicated lock object.

Write:

private [static] readonly object lockObject = new object();
private [static] readonly object subLockObject = new object();

instead. This guarantees that you have a unique object to lock on.

canton7
  • 37,633
  • 3
  • 64
  • 77
1

As mentioned here you should avoid using String instances as arguments for lock.

The reason for this is the fact that the runtime will use the same reference for strings that have the same value. This is what happens in your example.

Radu Diță
  • 13,476
  • 2
  • 30
  • 34