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# 线程 - 锁 - 如何检查锁的出现的主要内容,如果未能解决你的问题,请参考以下文章