Is Interlocked.CompareExchange really faster than a simple lock?

假如想象 提交于 2019-12-10 13:38:32

问题


I came across a ConcurrentDictionary implementation for .NET 3.5 (I'm so sorry I could find the link right now) that uses this approach for locking:

var current = Thread.CurrentThread.ManagedThreadId;
while (Interlocked.CompareExchange(ref owner, current, 0) != current) { }

// PROCESS SOMETHING HERE

if (current != Interlocked.Exchange(ref owner, 0))
        throw new UnauthorizedAccessException("Thread had access to cache even though it shouldn't have.");

Instead of the traditional lock:

lock(lockObject)
{
    // PROCESS SOMETHING HERE
}

The question is: Is there any real reason for doing this? Is it faster or have some hidden benefit?

PS: I know there's a ConcurrentDictionary in some latest version of .NET but I can't use for a legacy project.

Edit:

In my specific case, what I'm doing is just manipulating an internal Dictionary class in such a way that it's thread safe.

Example:

public bool RemoveItem(TKey key)
{
    // open lock
    var current = Thread.CurrentThread.ManagedThreadId;
    while (Interlocked.CompareExchange(ref owner, current, 0) != current) { }


    // real processing starts here (entries is a regular `Dictionary` class.
    var found = entries.Remove(key);


    // verify lock
    if (current != Interlocked.Exchange(ref owner, 0))
        throw new UnauthorizedAccessException("Thread had access to cache even though it shouldn't have.");
    return found;
}

As @doctorlove suggested, this is the code: https://github.com/miensol/SimpleConfigSections/blob/master/SimpleConfigSections/Cache.cs


回答1:


There is no definitive answer to your question. I would answer: it depends.

What the code you've provided is doing is:

  1. wait for an object to be in a known state (threadId == 0 == no current work)
  2. do work
  3. set back the known state to the object
  4. another thread now can do work too, because it can go from step 1 to step 2

As you've noted, you have a loop in the code that actually does the "wait" step. You don't block the thread until you can access to your critical section, you just burn CPU instead. Try to replace your processing (in your case, a call to Remove) by Thread.Sleep(2000), you'll see the other "waiting" thread consuming all of one of your CPUs for 2s in the loop.

Which means, which one is better depends on several factors. For example: how many concurrent accesses are there? How long the operation takes to complete? How many CPUs do you have?

I would use lock instead of Interlocked because it's way easier to read and maintain. The exception would be the case you've got a piece of code called millions of times, and a particular use case you're sure Interlocked is faster.

So you'll have to measure by yourself both approaches. If you don't have time for this, then you probably don't need to worry about performances, and you should use lock.




回答2:


Your CompareExchange sample code doesn't release the lock if an exception is thrown by "PROCESS SOMETHING HERE".

For this reason as well as the simpler, more readable code, I would prefer the lock statement.

You could rectify the problem with a try/finally, but this makes the code even uglier.

The linked ConcurrentDictionary implementation has a bug: it will fail to release the lock if the caller passes a null key, potentially leaving other threads spinning indefinitely.

As for efficiency, your CompareExchange version is essentially a Spinlock, which can be efficient if threads are only likely to be blocked for short periods. But inserting into a managed dictionary can take a relatively long time, since it may be necessary to resize the dictionary. Therefore, IMHO, this isn't a good candidate for a spinlock - which can be wasteful, especially on single-processor system.




回答3:


The docs for the Interlocked class tell us it

"Provides atomic operations for variables that are shared by multiple threads. "

The theory is an atomic operation can be faster than locks. Albahari gives some further details on interlocked operations stating they are faster.

Note that Interlocked provides a "smaller" interface than Lock - see previous question here




回答4:


Yes. The Interlocked class offer atomic operations which means they do not block other code like a lock because they don't really need to. When you lock a block of code you want to make sure no 2 threads are in it at the same time, that means that when a thread is inside all other threads wait to get in, which uses resources (cpu time and idle threads). The atomic operations on the other hand do not need to block other atomic operations because they are atomic. It's conceptually a one CPU operation, the next ones just go in after the previous, and you're not wasting threads on just waiting. (By the way, that's why it's limited to very basic operations like Increment, Exchange etc.)

I think a lock (which is a Monitor underneath) uses interlocked to know if the lock is already taken, but it can't know that the actions inside it can be atomic.

In most cases, though, the difference is not critical. But you need to verify that for your specific case.




回答5:


A little bit late... I have read your sample but in short:

Fastest to slowest MT sync:

  • Interlocked.* => This is a CPU atomic instruction. Can't be beat if it is sufficient for your need.
  • SpinLock => Uses Interlocked behind and is really fast. Uses CPU when wait. Do not use for code that wait long time (it is usually used to prevent thread switching for lock that do quick action). If you often have to wait more than one thread cycle, I would suggest to go with "Lock"
  • Lock => The slowest but easier to use and read than SpinLock. The instruction itself is very fast but if it can't acquire the lock it will relinquish the cpu. Behind the scene, it will do a WaitForSingleObject on a kernel objet (CriticalSection) and then Window will give cpu time to the thread only when the lock will be freed by the thread that acquired it.

Have fun with MT!



来源:https://stackoverflow.com/questions/20800954/is-interlocked-compareexchange-really-faster-than-a-simple-lock

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!