Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

locking by target object self

I'm curious about the difference below two example.

case 1) locking by readonly object

private readonly object key = new object();
private List<int> list = new List<int>;
private void foo()
{
    lock(key){
        list.add(1);
    }   
}

case2) locking by target object itself

private List<int> list = new List<int>;
private void foo()
{
    lock(list){
        list.add(1);
    }   
}

are both cases thread-safe enough? i'm wondering if garbage collector changes the address of list variable(like 0x382743 => 0x576382) at sometime so that it could fail thread-safe.

like image 555
Younghyo Kim Avatar asked Nov 18 '16 01:11

Younghyo Kim


People also ask

How to lock a method in Python?

A lock can be locked using the acquire() method. Once a thread has acquired the lock, all subsequent attempts to acquire the lock are blocked until it is released. The lock can be released using the release() method.

What are lock objects in Python?

A Lock object can not be acquired again by any thread unless it is released by the thread which is accessing the shared resource. An RLock object can be acquired numerous times by any thread. A Lock object can be released by any thread. An RLock object can only be released by the thread which acquired it.

Why do we lock objects in C#?

The lock statement acquires the mutual-exclusion lock for a given object, executes a statement block, and then releases the lock. While a lock is held, the thread that holds the lock can again acquire and release the lock. Any other thread is blocked from acquiring the lock and waits until the lock is released.


2 Answers

So long as List<T> does not have within its code any lock(this) statements the two functions will behave the same.

However, because you don't always know if a object locks on itself or not without looking through it's source code it is safer to just lock on a separate object.

One thing of note, classes that inherit from ICollection have a SyncRoot property which is explicitly the object you are supposed to lock on if you want to put a lock on the collection without using a seperate object.

private List<int> list = new List<int>;
private void foo()
{
    lock(((ICollection)list).SyncRoot){
        list.add(1);
    }   
}

This internally is just doing the same thing as you did and created a separate new Object() to lock on.

like image 58
Scott Chamberlain Avatar answered Oct 01 '22 16:10

Scott Chamberlain


In both cases, foo() is thread-safe. But locking on separate readonly object (case 1) is preferred, because

  • you don't have to bother about whether list is not null (lock would then fail)
  • you don't have to bother about whether list is re-assigned (assigning new instance of List<T> to list could cause some troubles, e.g. loss of atomicity of locked block of code)
  • you don't have to bother about whether you pass list to third-party code (e.g. as result of some function), as it is good practice to lock only on objects you have under your exclusive control (it could lead to deadlock if another piece of could would lock on this object too).
  • when lock protects more objects at once (e.g. list1, list2, etc.), locking just on one of them would still work (if you would lock consistently on the same object everywhere), but would lead to less readable and harder to understand code.

Lock is guaranteed to work properly even if garbage collector moves object you are locking on.

like image 38
Ňuf Avatar answered Oct 01 '22 16:10

Ňuf