Why can't we lock on a value type?

前端 未结 9 1079
天涯浪人
天涯浪人 2020-12-08 06:27

I was trying to lock a Boolean variable when I encountered the following error :

\'bool\' is not a reference type as require

相关标签:
9条回答
  • 2020-12-08 06:42

    The following is taken from MSDN:

    The lock (C#) and SyncLock (Visual Basic) statements can be used to ensure that a block of code runs to completion without interruption by other threads. This is accomplished by obtaining a mutual-exclusion lock for a given object for the duration of the code block.

    and

    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.

    I would assume that this is in part because the lock mechanism uses an instance of that object to create the mutual exclusion lock.

    0 讨论(0)
  • 2020-12-08 06:47

    Because value types don't have the sync block that the lock statement uses to lock on an object. Only reference types carry the overhead of the type info, sync block etc.

    If you box your reference type then you now have an object containing the value type and can lock on that object (I expect) since it now has the extra overhead that objects have (a pointer to a sync block that is used for locking, a pointer to the type information etc). As everyone else is stating though - if you box an object you will get a NEW object every time you box it so you will be locking on different objects every time - which completely defeats the purpose of taking a lock.

    This would probably work (although it's completely pointless and I haven't tried it)

    int x = 7;
    object boxed = (object)x;
    
    //thread1:
    lock (boxed){
     ...
    }
    //thread2:
    lock(boxed){
    ...
    }
    

    As long as everyone uses boxed and the object boxed is only set once you would probably get correct locking since you are locking on the boxed object and it's only being created once. DON'T do this though.. it's just a thought exercise (and might not even work - like I said, I haven't tested it ).

    As to your second question - No, the value is not copied for each thread. Both threads will be using the same boolean, but the threads are not guaranteed to see the freshest value for it (when one thread sets the value it might not get written back to the memory location immediately, so any other thread reading the value would get an 'old' result).

    0 讨论(0)
  • 2020-12-08 06:52

    It expands to:

    System.Threading.Monitor.Enter(x);
    try {
       ...
    }
    finally {
       System.Threading.Monitor.Exit(x);
    }
    

    Although they would compile, Monitor.Enter/Exit require a reference type because a value type would be boxed to a different object instance each time so each call to Enter and Exit would be operating on different objects.

    From the MSDN Enter method page:

    Use Monitor to lock objects (that is, reference types), not value types. When you pass a value type variable to Enter, it is boxed as an object. If you pass the same variable to Enter again, it is boxed as a separate object, and the thread does not block. In this case, the code that Monitor is supposedly protecting is not protected. Furthermore, when you pass the variable to Exit, still another separate object is created. Because the object passed to Exit is different from the object passed to Enter, Monitor throws SynchronizationLockException. For more information, see the conceptual topic Monitors.

    0 讨论(0)
  • 2020-12-08 06:52

    If you're asking conceptually why this isn't allowed, I would say the answer stems from the fact that a value type's identity is exactly equivalent to its value (that's what makes it a value type).

    So anyone anywhere in the universe talking about the int 4 is talking about the same thing - how then can you possibly claim exclusive access to lock on it?

    0 讨论(0)
  • 2020-12-08 06:59

    You cannot lock a value type because it doesn't have a sync root record.

    Locking is performed by CLR and OS internals mechanisms that rely upon an object having a record that can only be accessed by a single thread at a time - sync block root. Any reference type would have:

    • Pointer to a type
    • Sync block root
    • Pointer to the instance data in heap
    0 讨论(0)
  • 2020-12-08 07:01

    I think this is one of those cases where the answer to why is "because a Microsoft engineer implemented it that way".

    The way locking works under the hood is by creating a table of lock structures in memory and then using the objects vtable to remember the position in the table where the required lock is. This gives the appearance that every object has a lock when in fact they don't. Only those that have been locked do. As value types don't have a reference there is no vtable to store the locks position in.

    Why Microsoft chose this strange way of doing things is anyone's guess. They could have made Monitor a class you had to instantiate. I'm sure I have seen an article by an MS employee that said that on reflection this design pattern was a mistake, but I can't seem to find it now.

    0 讨论(0)
提交回复
热议问题