C# 线程 - 锁 - 如何检查锁的出现

Posted

技术标签:

【中文标题】C# 线程 - 锁 - 如何检查锁的出现【英文标题】:C# Threading - lock - how to check lock occurrences 【发布时间】:2012-10-13 22:41:47 【问题描述】:

我在我的应用程序中使用多线程锁定对象。

我如何检查其他线程尝试处理锁定对象的次数,或者我在尝试更新锁定对象上浪费了多少时间?

我的代码基于此处的最佳答案:

Mutliple threads updating array

编辑:复制代码:

float[] bestResult;
object sync = new Object();

lock (sync) 

    if (bestResult[0] > calculatedData[0]) 
        bestResult = calculatedData;
    

【问题讨论】:

【参考方案1】:

如here 所述,Concurrency Visualizer 是开始调查锁争用的好工具。不要害怕这篇博文来自 2010 年。同样的原则仍然适用于新版本的 Visual Studio 的并发可视化工具。

【讨论】:

【参考方案2】:

我不是线程专家,但为了获得其他线程尝试处理对象的次数,您可能必须实现比Lock 更原始的锁定机制版本。我在下面用一个紧密循环的 Monitor.TryEnter 给了它一个镜头,欢迎使用 cmets。

当然,单独实现这样的事情很容易导致更长的阻塞时间和更多的阻塞,以便获得您想要的计数,并且基于这种实现肯定与锁定在内部的工作方式不同的事实。不管怎样,我花了时间,所以我要发布它。

   class Program
    
        static object lockObj = new object();
        static void Main(string[] args)
        
            System.Threading.Thread t = new System.Threading.Thread(proc);
            System.Threading.Thread t2 = new System.Threading.Thread(proc);
            t.Start();
            t2.Start();
            t.Join();
            t2.Join();
            Console.WriteLine("Total locked up time = " + (LockWithCount.TotalWaitTicks / 10000) + "ms");
            Console.WriteLine("Total blocks = " + LockWithCount.TotalBlocks);
            Console.ReadLine();
        

        static void proc()
        
            for (int x = 0; x < 100; x++)
            
                using (new LockWithCount(lockObj))
                
                    System.Threading.Thread.Sleep(10);
                
            
        
    

上面向您展示了如何使用 LockWithCount,将现有的 Lock() 替换为 using(new LockWithCount(x))

 class LockWithCount : IDisposable

    static System.Diagnostics.Stopwatch watch = new System.Diagnostics.Stopwatch();
    object lockObj;
    public static long TotalWaitTicks = 0;
    public static long TotalBlocks = 0;
    static LockWithCount()
    
        watch.Start();
    

    public LockWithCount(object obj)
    
        lockObj = obj;
        long startTicks = watch.ElapsedTicks;
        if (!System.Threading.Monitor.TryEnter(lockObj))
        
            System.Threading.Interlocked.Increment(ref TotalBlocks);
            System.Threading.Monitor.Enter(lockObj);
            System.Threading.Interlocked.Add(ref TotalWaitTicks, watch.ElapsedTicks - startTicks);
        
    


    public void Dispose()
    
        System.Threading.Monitor.Exit(lockObj);
    

【讨论】:

忙于等待锁是高度 CPU 密集型的。这会起作用,但如果锁被轻微争用,它会降低性能。更好的解决方案是执行单个 Monitor.TryEnter,如果失败,设置 blocked 标志并执行 Monitor.Enter @Jim Mischel 好主意,在您实际等待时,就实现而言,这将使其与锁定非常接近。我会更新的。【参考方案3】:

问题是如何确定锁请求发生的次数,或者由于锁争用而浪费的时间。

对所提问题的回答是您使用诸如 Visual Studio 高级版提供的分析器。可能存在其他 .NET 分析器。

在每个 lock 语句中添加计数/计时代码是不切实际的,因为它会有自己的锁定问题。因此,在没有分析器的情况下,您必须执行静态分析。这不是那么可怕。嵌套循环是一个重要线索。

在服务器设计中,锁争用最为严重。令人高兴的是,用户会话状态是会话私有的。如果您使用的是 APM(异步编程模型 - 本质上是回调),那么只要您在处理程序结束之前不调用 socket.BeginRead,从会话的角度来看,状态操作实际上是单线程的。因此,在这些条件下,只有在会话的建立和拆除时才需要锁定。在会话中完全没有必要。

这就是为什么我更喜欢 APM 而不是处理并发执行的更新、更时尚的方法。

【讨论】:

感谢您的回复。我从来没有在 .NET 中使用过像分析器这样的工具,我也不知道在我的 VS2008 标准版中是否有这样的工具。我认为@FMM 回复对我来说已经足够了。【参考方案4】:

System.Diagnostics.Stopwatch 类可能会在这方面为您提供帮助:

float[] bestResult;
object sync = new Object();
var sw = new System.Diagnostics.Stopwatch();
sw.Start();

lock (sync) 

    sw.Stop();
    if (bestResult[0] > calculatedData[0]) 
        bestResult = calculatedData;
    


Console.WriteLine("Time spent waiting: " + sw.Elapsed);

【讨论】:

尽管我在下面给出了答案,但如果您想在没有探查器的情况下进行测量,那么 FMM 是我能看到的唯一实用答案。 如果您正在探索整个并发问题,您可能会发现这很方便。这是“C# 5.0 In A Nutshell”albahari.com/threading 中关于线程的整个部分

以上是关于C# 线程 - 锁 - 如何检查锁的出现的主要内容,如果未能解决你的问题,请参考以下文章

线程锁与其他用法

HashMap并发出现死循环 及 减少锁的竞争

多线程安全问题及各种锁

Java-JUC:同步锁的几种方式

多线程下解决资源竞争的7种方法

Synchronized锁的是什么?