【发布时间】:2021-07-03 21:40:14
【问题描述】:
我搜索了一个答案,并且我大致了解如何在多线程环境中使用锁等。这个问题困扰了我很长时间,我想我不是唯一一个。 TL;最后是 DR。
在我的情况下,我想防止从多个线程调用的方法在被另一个线程调用时被执行。
现在 C# 中的正常锁定场景如下所示:
static readonly object _locker = new object();
private static int counter;
public void Increase()
{
lock (_locker)
{
_counter++;//Do more than this here
}
}
据我了解,object _locker 充当bool,指示当前是否正在执行该方法。如果方法是“免费的”,则将其设置为锁定并执行方法并随后释放。如果方法被锁定,等待解锁,锁定,执行,解锁。
附加问题 1: 重复调用此方法是否保证了类似队列的行为?忽略父线程中的阻塞可能会导致问题的事实。想象Increase() 是父线程中的最后一次调用。
附加问题 2: 以布尔方式使用 object 感觉很奇怪。是否每个object 都包含一个“正在使用”标志,而一个“原始”object 仅用于包含此标志?为什么不Boolean?
附加问题3:lock()如何修改readonly?
功能上我也可以这样写:
static Boolean _locker = false;
private static int counter;
public void Increase()
{
while(_locker)//Waits for lock==0
{
}
_locker = true;//Sets lock=1
_counter++;//Do more than this here
_locker = false;//Sets lock=0
}
虽然锁示例看起来复杂且安全,但第二个示例感觉不对劲,不知何故在我脑海中敲响了警钟。
这个方法是否有可能在完全相同的 CPU 周期被两个内核同时执行?
我知道这是“但有时”的极端。我相信操作系统调度程序确实将线程从一个应用程序拆分到多个内核,那么为什么不应该同时在两个内核上执行汇编指令“加载_locked 的值以进行比较”?即使隔一个周期进入该方法,“read for comparison”和“write true to _locked”也会同时执行。
这甚至没有考虑到一行 C# 可以/将转换为多个汇编指令,并且在确认 locked==0 并写入 locked=1 后可能会中断一个线程。因为一行C#可以产生很多汇编指令,连lock()都可能被打断?
显然,这些问题以某种方式解决或避免了,我非常感谢您解释我的思维过程在哪里出错或我缺少什么。
TL;DR 两个 CPU 内核可以同时执行 lock() 语句吗?我无法解释软件在没有大的性能影响的情况下避免这种情况。
【问题讨论】:
-
你的第二个代码 sn-p 有缺陷,会出现并发问题。使用锁()。非竞争锁足够快;在您测量之前不要假设您有性能问题。
-
TLDR;锁使用 CPU 特性来确保一次只有一个线程可以进入锁。
objects包含实现此功能所需的任何内部运行时状态,您无需知道那是什么。为什么反对?因为 Java 就是这样做的。 -
lock()保证是原子的(在检查它是否被锁定和设置锁定状态之间不能被中断),但是你的 while 循环不是。反馈您的问题,不要在一个帖子中提出多个问题。
标签: c# multithreading assembly locking cpu-architecture