我看到,对于使用不是线程安全的对象,我们用这样的锁包装代码:
private static readonly Object obj = new Object(); lock (obj) { // thread unsafe code }
那么当多个线程访问相同的代码时会发生什么(让我们假设它在ASP.NET Web应用程序中运行) . 他们排队了吗?如果是这样,他们会等多久?
使用锁会对性能产生什么影响?
lock实际上是隐藏Monitor类 .
不,他们没有排队,他们正在睡觉
表单的锁定声明
lock (x) ...
其中x是引用类型的表达式,恰好相当于
var temp = x; System.Threading.Monitor.Enter(temp); try { ... } finally { System.Threading.Monitor.Exit(temp); }
你只需要知道他们正在等待彼此,只有一个线程会进入锁定块,其他线程将等待...
Monitor完全用.net编写,所以它足够快,也可以查看class Monitor与reflector了解更多详情
锁将阻止其他线程执行锁定块中包含的代码 . 线程必须等到锁块内的线程完成并释放锁定 . 这确实会对多线程环境中的性能产生负面影响 . 如果确实需要这样做,则应确保锁定块中的代码可以非常快速地处理 . 您应该尽量避免访问数据库等昂贵的活动 .
性能影响取决于您的锁定方式 . 您可以在此处找到一个很好的优化列表:http://www.thinkingparallel.com/2007/07/31/10-ways-to-reduce-lock-contention-in-threaded-programs/
基本上你应该尝试尽可能少地锁定,因为它会让你的等待代码进入睡眠状态 . 如果您在锁定中有一些繁重的计算或持久的代码(例如文件上载),则会导致巨大的性能损失 .
lock 语句由C#3.0转换为以下内容:
lock
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:
Monitor.Enter
使用Enter获取作为参数传递的对象的监视器 . 如果另一个线程在对象上执行了Enter但尚未执行相应的Exit,则当前线程将阻塞,直到另一个线程释放该对象 . 如果没有阻塞,同一个线程不止一次调用Enter是合法的;但是,在等待对象的其他线程将解除阻塞之前,必须调用相同数量的Exit调用 .
Monitor.Enter 方法将无限期等待;它不会超时 .
它比你想象的简单 .
根据Microsoft: lock 关键字确保一个线程不进入代码的关键部分而另一个线程处于临界区 . 如果另一个线程试图输入锁定的代码,它将等待,阻止,直到该对象被释放 .
lock 关键字在块的开头调用Enter,在块的末尾调用Exit . lock 关键字实际上在后端处理Monitor类 .
例如:
private static readonly Object obj = new Object(); lock (obj) { // critical section }
在上面的代码中,第一个线程进入临界区,然后它将锁定 obj ,当其他线程尝试进入时,它也将尝试锁定已被第一个线程锁定的 obj ,我将不得不等待第一个线程释放 obj . 当第一个将离开时,其他线程将锁定 obj 并将进入临界区 .
obj
lock 语句被转换为对Monitor的 Enter 和 Exit 方法的调用 .
Enter
Exit
lock 语句将无限期地等待释放锁定对象 .
lock语句中的部分只能由一个线程执行,因此所有其他线程将无限期地等待持有锁完成的线程 . 这可能导致所谓的死锁 .
8 回答
lock实际上是隐藏Monitor类 .
不,他们没有排队,他们正在睡觉
表单的锁定声明
其中x是引用类型的表达式,恰好相当于
你只需要知道他们正在等待彼此,只有一个线程会进入锁定块,其他线程将等待...
Monitor完全用.net编写,所以它足够快,也可以查看class Monitor与reflector了解更多详情
锁将阻止其他线程执行锁定块中包含的代码 . 线程必须等到锁块内的线程完成并释放锁定 . 这确实会对多线程环境中的性能产生负面影响 . 如果确实需要这样做,则应确保锁定块中的代码可以非常快速地处理 . 您应该尽量避免访问数据库等昂贵的活动 .
性能影响取决于您的锁定方式 . 您可以在此处找到一个很好的优化列表:http://www.thinkingparallel.com/2007/07/31/10-ways-to-reduce-lock-contention-in-threaded-programs/
基本上你应该尝试尽可能少地锁定,因为它会让你的等待代码进入睡眠状态 . 如果您在锁定中有一些繁重的计算或持久的代码(例如文件上载),则会导致巨大的性能损失 .
lock
语句由C#3.0转换为以下内容:在C#4.0 this has changed中,它现在生成如下:
你可以找到更多关于
Monitor.Enter
做here的信息 . 引用MSDN:Monitor.Enter
方法将无限期等待;它不会超时 .它比你想象的简单 .
根据Microsoft:
lock
关键字确保一个线程不进入代码的关键部分而另一个线程处于临界区 . 如果另一个线程试图输入锁定的代码,它将等待,阻止,直到该对象被释放 .lock
关键字在块的开头调用Enter,在块的末尾调用Exit .lock
关键字实际上在后端处理Monitor类 .例如:
在上面的代码中,第一个线程进入临界区,然后它将锁定
obj
,当其他线程尝试进入时,它也将尝试锁定已被第一个线程锁定的obj
,我将不得不等待第一个线程释放obj
. 当第一个将离开时,其他线程将锁定obj
并将进入临界区 .lock
语句被转换为对Monitor的Enter
和Exit
方法的调用 .lock
语句将无限期地等待释放锁定对象 .lock语句中的部分只能由一个线程执行,因此所有其他线程将无限期地等待持有锁完成的线程 . 这可能导致所谓的死锁 .