开发者

Few confusing things about locks

I know how to use locks in my app, but there still few things that I don't quite understand about locking ( BTW - I know that the lock statement is just a shorthand notation for working with Monitor class type ).

From http://msdn.microsoft.com/en-us/library/ms173179.aspx:

 public class TestThreading
 {
     private System.Object lockThis = new System.Object();

     public void Function()
     {

         lock (lockThis)
         {
            // Access thread-sensitive resources.
         }
     }
  }

The argument provided to the lock keyword must be an object based on a reference type, and is used to define the scope of the lock. In the example above, the lock scope is limited to this function because no references to the object lockThis exist outside the function. If such a reference did exist, lock scope would extend to that object.

a) I don’t understand how lockThis object defines the scope of the lock. Scope of the lock is all the code between lock(lockThis){ and adjacent }, so what exactly is it meant by “lock scope would extend to that object”?

b) What does the term locking on object lockThis mean? Simply that we use a reference to lockThis to lock a region of code? Thus, the term doesn't suggest that we locked lockThis object?

thanx

Replying to David Morton:

I apologize if my reply is a bit long winded, but I couldn't think of any other way to word my questions and still be somewhat coherent:

  1. The scope of the lock, in this situation, is the individual instance of the class itself. This is opposed to crossing all instance of the specific class. You could have your lock cross all instances TestThreading by making lockThis static. That's what's meant by the "scope" of the lock: whether it's applicable to a single instance, or applicable to every instance of a particular type.

Other objects could still access lockThis from another thread, but they wouldn't be able to process code that is surrounded by a lock on that object.

If we call the code surrounded by lock ( located inside TestThreading.Function ) C, then since TestThreading.Function is not static, each TestThreading instance has its own copy of C. But if TestThreading.Function was static, then all TestThreading instances would share the same copy of C. As an analogy, if C is a room and if TestThreading.Function is not static, then each TestThreading instance would have its own C room, but if function was static, then all TestThreading instances would share the same C room.

Following that analogy, I interpret lockThis as a key to room C. If lockThis is static and if TestThreading.Function is not static, then all TestThreading instances would use same key to enter their own C rooms.

  • Thus, I don’t see any significance in lockThis being static or not, since in either case each TestThreading instance will use lockThis to enter its own room C ( assuming TestThreading.Function is not static ). Thus, following that logic, shouldn't the scope of the lock always be individual instance of the class (assuming TestThreading.Function is not static )?

  • Similarly, I don’t see any significance in lockThis being private or public, since again TestThreading instance will use lockThis to enter its own room C( assuming TestThreading.Function is not static)

Second reply to David Morton

In response to your response: There is a significance. If TestThreading.Function is static, then lockThis has to be static, otherwise, TestThreading.Function cannot access lockThis at all.

I’m not sure what you mean by TestThreading.Function not being able to access lockThis?! Again assume we call the code surrounded by lock ( located inside TestThreading.Function ) C. If TestThreading.Function is static and thus there is only one room, but lockThis is non-static, and if we have the following definition:

 public class TestThreading
 {
    private System.Object lockThis = new System.Object();

    public static  void Function()
    {

        lock (new TestThreading().lockThis)
        {
           // Access thread-sensitive resources.
        }
    }
 }

,then whenever some thread would access room C, it would use a new key. Thus, if 100 threads accessed room C, then same room would be opened using 100 different keys?! So the code does work, it just doesn’t make much sense, since there would be no synchronization at all between threads?!

The scope of the lock, in this situation, is the individual instance of the class itself. This is opposed to crossing all instance of the specific class. You could have your lock cross all instances TestThreading by making lockThis static.

I was confused with the term scope of the lock, since I’ve inte开发者_JS百科rpret it as: if the lockThis was static, then all TestThread instances would share the same room (aka lock), even though TestThread.Function isn’t static. Assuming I understand it correctly now, then the term lock is referring to the key (thus lock doesn’t refer to the room?! ) used for opening the doors?

As such, if we assume that lockThis is static and TestThread.Function is not static, then the scope of the key/lock is all TestThread instances, which means that all TestThread instances share the same key and thus when one TestThread instance opens the door to its room using this key, the other instances won’t be able to open the doors to their rooms until first instance releases that key?

Third reply to David Morton

No, if Function is static, then all TestThread instances would share the same room, if lockThis is static, then all TestThread instances would share the same key.

Do you agree that the word lock used in a sentence scope of the lock at least in a way refers to the key ( key being lockThis instance )?

The room is the code that is executed within the lock, not the lockThis object itself, that's simply the key.

[Defense mode ON] That’s what I’ve said in my last reply. ;) [Defense mode OFF]


  1. The scope of the lock, in this situation, is the individual instance of the class itself. This is opposed to crossing all instance of the specific class. You could have your lock cross all instances TestThreading by making lockThis static. That's what's meant by the "scope" of the lock: whether it's applicable to a single instance, or applicable to every instance of a particular type.

  2. "Locking on object thisObject" simply means to use thisObject as the object that determines whether or not we're in the lock. The term doesn't suggest that we "locked" the lockThis object. I think your estimation is correct here. Other objects could still access lockThis from another thread, but they wouldn't be able to process code that is surrounded by a lock on that object.

Since we're responding to each other, here's a bit more information:

Let's take the "room and key" analogy you outlined above, and extend it for each and every possibility. We have a few different situations:

  1. static function, static lock object - This is analogous to having one room, with only one key.
  2. instance function, static lock object - This would be like having several rooms that share the same key... like a master key, for example.
  3. instance function, instance lock object - This would be like having several rooms, each with it's own key... like a dormitory or hotel.
  4. static function, instance lock object - impossible. You can't access an instance field from a static context.

Hopefully, the above scenarios outline how these would work together.

In response to the second response

I’m not sure what you mean by TestThreading.Function not being able to access lockThis?!

It can't access a useful lockThis. A static method locking on an instance that gets thrown away when the lock is finished is useless. In the scenario you set up, each time the door is approached, the lock "gives away" a new key, and says "okay, I accept the key I just gave you... you can come on in". In that situation, it's guarding the "room" too loosely, giving everyone access whenever they want it. In other words, there's no reason to even have a lock statement in that second example. It's like the room has a dynamically increasing number of doors, used just once, that has the key taped to it. An infinite number of people can enter whenever they want. There's no difference between your code and the following, really (there are, but this is so that you can hopefully get the concept:

public class TestThreading
{
    private object lockThis = new object();

    public static void Function() 
    {
        lockThis = new object();
        lock (lockThis)
        {
            // access something   
        }
    }
}

What I've done in that example above is basically throw away and re-key the lock right before I use it in the door. That's not useful. You're right, there wouldn't be any synchronization of threads, and in that sense, I would say that the code doesn't really work at all, as the whole point of the lock is to synchronize threads.

if the lockThis was static, then all TestThread instances would share the same room (aka lock), even though TestThread.Function isn’t static. Assuming I understand it correctly now, then the term lock is referring to the key (thus lock doesn’t refer to the room?! ) used for opening the doors?

No, if Function is static, then all TestThread instances would share the same room, if lockThis is static, then all TestThread instances would share the same key.

lock (key)
{
   // room
}

The room is the code that is executed within the lock, not the lockThis object itself, that's simply the key.

As such, if we assume that lockThis is static and TestThread.Function is not static, then the scope of the key/lock is all TestThread instances, which means that all TestThread instances share the same key and thus when one TestThread instance opens the door to its room using this key, the other instances won’t be able to open the doors to their rooms until first instance releases that key?

Yes.


To illustrate the differences in locking over static or non-static objects, consider:

public class TestThreading
{
    private object lockThis = new object();
    private static object lockStatic = new object();
    private Stream UIStream;
    private List<int> calcStuff;

    public void DoStuff1()
    {
        lock (lockThis)
        {
            // Do stuff with calcStuff that is unique to this instance
            // it's okay to have different instances calculating their own stuff
        }
    }

    public void DoStuff2()
    {
        lock (lockStatic)
        {
            // Do stuff with the UI,
            // which you don't want multiple threads accessing at once
        }
    }
}

especially in a situation like

var A = new TestThreading();
var B = new TestThreading();

((Action)(A.DoStuff1)).BeginInvoke(null,null); // These can go concurrently
((Action)(B.DoStuff1)).BeginInvoke(null,null); // just fine

((Action)(A.DoStuff1)).BeginInvoke(null,null); // These will have to wait
((Action)(A.DoStuff1)).BeginInvoke(null,null);

((Action)(A.DoStuff2)).BeginInvoke(null,null); // These will have to wait
((Action)(B.DoStuff2)).BeginInvoke(null,null);


What that paragraph is talking about is what code has the ability to take the lock, not how long the lock is held (which that article refers to as lock duration).


David Morton's answer seems correct to me, but for a greater reference, see this website: Threading in C# It's a great resource for all things threading in C#, including examples of what not to do in a few cases. Very clear IMO.


a) Since every object of the TestThreading class will have it's own "lockThis" object, only that individual object will be in the scope of the lock b/c only that individual object can even reference "lockThis" (b/c it is private)

b) No, it means since the TestThreading object that called the lock is the only object that can "lock" the critical region (again, b/c it is private), "locking on object lockThis" means you are locking the TestThreading object using the lockThis private reference type.

For example, if instead you wrote this:

public class TestThreading
 {
     // private System.Object lockThis = new System.Object();

     public void Function()
     {

         lock (this)
         {
            // Access thread-sensitive resources.
         }
     }
  }

Not only would the individual object be able to lock the object using the Function method, EVERY CLASS THAT HAS A REFERENCE to the TestThreading object in use can lock using the Function method, b/c the TestThreading object would presumably be at least "internal" if not "public".

0

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜