Mehraban
Mehraban

Reputation: 3324

Lock on 2 objects togethere in c#

Is there any way to get a lock over 2 different objects together? I tried to use lock(obj1, obj2){...} but I got this error:

invalid expression term ','

UPDATE: As many of users told me to try and use just one lock, and I appreciate their advice because it's more preferable most of the time, I just want to show a case in which I think have a lock over 2 objects is more reasonable. Consider the case of having 2 queues shared among different threads. You need to avoid do Enqueue(item) and Dequeue() simultaneously. Now, at a particular section of your code, you want to get an element from one queue and insert it into the second queue. I know it's possible to do it like:

var itme;
lock(_lock1)
{
    item = q1.Dequeue();
    Monitor.Pulse(_lock1);
}
lock(_lock2)
{
    q2.Enqueue(item);
    Monitor.Pulse(_lock2);
}

But I think get a lock over both _lock1 and _lock2 is more readable and clean.

Upvotes: 1

Views: 635

Answers (3)

Brian Gideon
Brian Gideon

Reputation: 48949

The syntax for using lock with two (or more) different objects is as follows.

lock (a) lock (b)
{
}

However, this type of usage is generally discouraged. There are a couple of reasons for this.

  • The order the locks are declared must remain consistent everywhere otherwise a deadlock might occur.
  • It is almost always unnecessary.
  • It is a code smell.

The first reason is really a cop-out in my opinion. There are an infinite number of ways that a mistake can be made while developing. This is just one among many. And, in fact, it is actually quite easy (relative to the other kinds of mistakes that can be made) to get right. Just make sure the locks are always acquired in the same order. Plus, there is nothing inherently dangerous about using nested locks (again, as long as it is done correctly). Nested locks are acquired all the time. It is just that it is almost always done implicitly by calling into other classes or as the execution proceeds through different stack frames.

It is the second and third reasons that really matter here. Acquiring two locks in the same section of code is unnecessary because one lock is almost always sufficient. In fact, I do not think I have ever needed to acquire two locks in the same section of code; at least not explicitly. It is a code smell because it usually means you are thinking about the problem wrong. It could mean the code is convoluted or that you are using strange code paths. Structuring the code so that it only requires one lock will likely make the code much easier to follow.

On a separate note I too wonder if you are confused about the nature of how a lock works. If you are thinking that the object instance used in the lock keyword is protected from simultaneous access then you are wrong. That is not how the lock keyword works. The object referenced by the lock is intended to mark a critical section of code that is guaranteed not to execute simultaneously with another (or same) section marked with the same object reference.

Update:

I took a look at your example and the first thing that jumps out at me is that this code may not be thread-safe. I cannot see the full context involved though. All I see is that this hypothetical class is using two queues internally and is pulsing two different locks. I can make some inferences, but they might be wrong. I am having a hard time envisioning a situation where you might do such a thing inside a single class. But, with that said, have you considered what happens when the thread executing this code is preempted between the locks? That is an opportunity for your class to go into a half-baked state (the item is not in either queue anymore). What happens when another thread attempts to use this class? Will it gracefully handle the situation where an item is hanging in limbo? I would have to see more code to comment further, but seeing that the arrangement of the locks allows a half-baked state to begin with is huge red flag to me.

Upvotes: 1

Daniel A. White
Daniel A. White

Reputation: 190935

I would strongly urge you to use one lock to avoid deadlocks and you should lock on a separate reference, not on the objects you want to modify.

class MyClass
{
    private readonly object _lock = new object();
    private readonly List<int> _myList = new List<int>();
    private int _index;

    public void MyOperation()
    {
        lock(_lock) 
        {
            _index++;
        }
    }

    public void MyOperation2()
    {
        lock(_lock) 
        {
            _myList[_index] = 27;
        }
    }
}

Upvotes: 4

Kamil Budziewski
Kamil Budziewski

Reputation: 23087

Try this

lock(obj1)
{
   lock(obj2)
   {
        // here you have lock on obj1 and obj2
   }
}

lock accepts only one argument. Example above will lock two items in scope of second lock.

Maybe it's providing answer to question, but:

Locking here doesn't mean that for duration of the lock no other code on other thread can access or modify the object. If you lock an object any other thread can modify the object at the same time. What lock code block allows you to do is to make the code inside the lock block to be single entry i.e only one thread can execute the lock code block once and other threads who tries to execute the same code block will have to wait till the owner thread is done with executing the code block. So basically you really don't need to lock 2 or more objects in usual cases. By locking your purpose is to make the code block single entry

In short, as other mentioned this solution is useless, better is creating new object desired for locking.

Upvotes: 1

Related Questions