当锁没有争用时,lock(...)的成本有多高?

23

在研究双重检查锁定时,我看到了许多人建议跳过第一次检查并立即获取锁,在获取锁后再进行检查。

这让我想知道,当没有竞争时,lock (this.padlock) 是否“便宜”?


1
在做出这个决定时,您还必须评估:锁是否会无争议(例如,在锁内发生了多少事情,这将大大增加争用率)。根据情况,最好进行真正的双重检查,或者您可以选择无锁。由于引用是保证原子性的,将数据移动到不可变对象并取消引用可能非常聪明(在正确的场景中)。 - Marc Gravell
3个回答

25

我们可以对它进行测试...

我得到:

1000000000; 2164 (no lock)
1000000000; 23258 (lock)
21.094ns per lock

代码:

using System;
using System.Diagnostics;

static class P
{
    static void Main()
    {

        Test(1); // for JIT
        Test(1000000);
    }
    static readonly object syncLock = new object();
    static void Test(int count)
    {
        int j = 0;
        var watch = Stopwatch.StartNew();
        for(int i = 0 ; i < count ; i++)
        {
            for (int z = 0; z < 1000; z++)
                j++;
        }
        watch.Stop();
        long withoutMillis = watch.ElapsedMilliseconds;
        Console.WriteLine("{0}; {1} (no lock)", j, watch.ElapsedMilliseconds);

        j = 0;
        watch = Stopwatch.StartNew();
        for (int i = 0; i < count; i++)
        {
            for (int z = 0; z < 1000; z++ )
                lock (syncLock)
                {
                    j++;
                }
        }
        watch.Stop();
        long withMillis = watch.ElapsedMilliseconds;
        Console.WriteLine("{0}; {1} (lock)", j, watch.ElapsedMilliseconds);

        long deltaNano = (withMillis - withoutMillis) * 1000000;
                // nano = 1000 micro = 1000000 milli
        double perLockNano = deltaNano/(1000.0 * count);
        Console.WriteLine("{0}ns per lock", perLockNano);
    }
}

@JohannGerell Intel i7 四核 920 @ 2.67 GHz,运行 x64 .NET 4 在 Win7 上。 - Marc Gravell
@JohannGerell жіЁж„ҸпјҢжҲ‘жӯЈеңЁжү§иЎҢldsfld(lock(staticLockObject))пјҢиҖҢдёҚжҳҜldarg0 + ldfld(lock(this.instanceLockObject)) - дҪ еә”иҜҘиғҪеӨҹи°ғж•ҙжөӢиҜ•д»Ҙе…Ғи®ёиҝҷж ·еҒҡгҖӮ - Marc Gravell
@JohannGerell 如果我改为每个实例,即不是非常显著的跳跃,我会得到21.426ns/lock - Marc Gravell

10

4
好的,那很便宜(非常便宜)。 - H H

2

我写这篇文章非常快,没有测试过,只是为了传达一个条件锁定的一种方法,使用扩展方法和委托或Lambda函数。我也不能肯定这是否会自我破坏,这是有可能的。

我有一些代码在多线程进程和使用协作纤维模式线程模型的进程中运行(例如,存在多个永远不会异步执行的线程)。在协作纤维模式应用程序中,锁定是无意义和浪费的,因此这可能是避免在每个必须进行锁定的点上进行大量混乱逻辑的解决方案。

// Conditional Locking concept code

namespace SystemExtensions {

public static class LockMeUp
{
  private static bool isLockingEnabled = true;

  // If set to true, locking will be performed
  // by the extension methods below.

  internal static bool LockingEnabled
  {
    get
    {
      return isLockingEnabled;
    }
    set
    {
      isLockingEnbaled = value;
    }
  }

  static void CheckNull<TLock>( TLock target ) where TLock: class
  {
    if( target == null )
      throw new ArgumentNullException("target cannot be null");
  }

  // Invoke the supplied action on the supplied lock object

  public static void TryLock<TLock>( 
    this TLock target, 
    Action<TLock> action ) where TLock: class
  {
    CheckNull( target );
    if( isLockingEnabled )
    {
      lock( target )
      {
        action( target );
      }
    }
    else
    {
      action( target );
    }
  }

  // Invoke the supplied function on the supplied 
  // lock object and return result:   

  public static T TryLock<TLock, T>( 
    this TLock target, 
    Func<TLock, T> func ) where TLock: class
  {
    CheckNull( target );
    if( isLockingEnabled )
    {
      lock( target )
      {
        return func( target );
      }
    }
    else
    {
      return func( target );
    }
  }

  // Invoke the supplied function on the supplied lock object 
  // and another supplied argument, and return the result:    

  public static T TryLock<TLock, TArg, T>( 
    this TLock target, 
    Func<TLock, TArg, T> func, 
    TArg arg ) where TLock: class
  {
    CheckNull( target );
    if( isLockingEnabled )
    {
      lock( target )
      {
        return func( target, arg );
      }
    }
    else
    {
      return func( target, arg );
    }
  }

  // Invoke the supplied action on the supplied lock object 
  // and another supplied argument:   

  public static void TryLock<TLock, TArg>( 
    this TLock target, 
    Action<TLock, TArg> func, 
    TArg arg )  where TLock: class
  {
    CheckNull( target );
    if( isLockingEnabled )
    {
      lock( target )
      {
        func( target, arg );
      }
    }
    else
    {
      func( target, arg );
    }
  } 
}

///// Example:

public static class SharedList<T>
{
  private static List<T> items = new List<T>();

  public static bool Remove( T item )
  {
    return items.TryLock( (list, item) => list.Remove( item ), item );
  }

  public static T GetItemAt( int index )
  {
    return items.TryLock( (list, i) => list[i], index );
  }

  public static bool Contains( T item )
  {
    return items.TryLock( (list, it) => list.Contains( it ), item );
  }

  public static void Add( T item )
  {
    items.TryLock( (list, item) => list.Add( item ) );
  }
}

} // namespace

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接