我看到,对于使用非线程安全的对象,我们用这样的锁包装代码:
private static readonly Object obj = new Object();
lock (obj)
{
// thread unsafe code
}
那么当多个线程访问相同的代码时会发生什么(让我们假设它在ASP.NET Web应用程序中运行)。他们排队了吗?如果是这样,他们会等多久?
因使用锁而对性能产生什么影响?
答案 0 :(得分:399)
lock
语句由C#3.0翻译为以下内容:
var temp = obj;
Monitor.Enter(temp);
try
{
// body
}
finally
{
Monitor.Exit(temp);
}
在C#4.0 this has changed中,它现在生成如下:
bool lockWasTaken = false;
var temp = obj;
try
{
Monitor.Enter(temp, ref lockWasTaken);
// body
}
finally
{
if (lockWasTaken)
{
Monitor.Exit(temp);
}
}
您可以找到有关Monitor.Enter
here的更多信息。引用MSDN:
使用
Enter
获取监控器 作为参数传递的对象。如果 另一个线程执行了Enter
在对象上但尚未执行 相应的Exit
,即当前 线程将阻塞直到另一个 线程释放对象。它是 合法的线程可以调用Enter
不止一次Exit
阻塞;但是,数量相等 之前必须调用Monitor.Enter
个调用 等待对象的其他线程 将取消阻止。
{{1}}方法将无限期等待;它将不超时。
答案 1 :(得分:238)
比你想象的要简单。
根据Microsoft:
lock
关键字确保一个线程不进入代码的关键部分,而另一个线程处于临界区。如果另一个线程试图输入一个锁定的代码,它将等待,阻塞,直到该对象被释放。
lock
关键字在块的开头调用Enter
,在块的末尾调用Exit
。 lock
关键字实际上在后端处理Monitor
类。
例如:
private static readonly Object obj = new Object();
lock (obj)
{
// critical section
}
在上面的代码中,首先线程进入临界区,然后它将锁定obj
。当另一个线程试图进入时,它也会尝试锁定已被第一个线程锁定的obj
。我将不得不等待第一个线程释放obj
。当第一个线程离开时,另一个线程将锁定obj
并进入临界区。
答案 2 :(得分:43)
不,他们没有排队,他们正在睡觉
表单的锁定声明
lock (x) ...
其中x是引用类型的表达式,恰好等同于
var temp = x;
System.Threading.Monitor.Enter(temp);
try { ... }
finally { System.Threading.Monitor.Exit(temp); }
你只需知道他们正在等待对方,只有一个线程会进入锁定区域,其他线程将等待......
监视器完全用.net编写,因此速度足够快,还可以class Monitor查看reflector以获取更多详细信息
答案 3 :(得分:26)
锁定将阻止其他线程执行锁定块中包含的代码。线程必须等到锁定块内的线程完成并释放锁定。这确实会对多线程环境中的性能产生负面影响。如果确实需要这样做,则应确保锁定块中的代码可以非常快速地处理。您应该尽量避免访问数据库等昂贵的活动。
答案 4 :(得分:10)
性能影响取决于您锁定的方式。您可以在此处找到一个很好的优化列表:http://www.thinkingparallel.com/2007/07/31/10-ways-to-reduce-lock-contention-in-threaded-programs/
基本上你应该尝试尽可能少地锁定,因为它会让你的等待代码进入睡眠状态。如果您在锁中有一些繁重的计算或持久的代码(例如文件上传),则会导致巨大的性能损失。
答案 5 :(得分:7)
lock语句中的部分只能由一个线程执行,因此所有其他线程将无限期地等待持有锁完成的线程。这可能导致所谓的死锁。
答案 6 :(得分:7)
lock
语句已转换为对Monitor
的Enter
和Exit
方法的调用。
lock
语句将无限期地等待锁定对象被释放。
答案 7 :(得分:4)
答案 8 :(得分:0)
根据Microsoft's MSDN,该锁等效于:
object __lockObj = x;
bool __lockWasTaken = false;
try
{
System.Threading.Monitor.Enter(__lockObj, ref __lockWasTaken);
// Your code...
}
finally
{
if (__lockWasTaken) System.Threading.Monitor.Exit(__lockObj);
}
顺便说一句,您可以使用DynaLock在运行时动态创建和管理锁!并使用上下文概念指定边界。
DynaLock是开源的,其源代码可在GitHub
获得。答案 9 :(得分:-1)
lock阻止进入一个线程而另一个线程已经进入。通常,它用于多线程。我们可以说的最好的例子之一是“ Singleton”设计模式。
static Singleton instance = null;
static readonly object obj = new object();
public static Singleton GetInstance
{
get
{
if (instance == null)
{
lock (obj)
{
if (instance == null)
{
instance = new Singleton();
}
}
}
return instance;
}
}