C# 像Thread.MemoryBarrier(或Monitor.Enter)这样的方法调用如何改变编译器的工作方式?
据我所知,使用C# 像Thread.MemoryBarrier(或Monitor.Enter)这样的方法调用如何改变编译器的工作方式?,c#,multithreading,compilation,C#,Multithreading,Compilation,据我所知,使用lock(意思是:Monitor.Enter和Monitor.Exit)在其周围添加了完整的内存围栏,以避免关键区域周围不必要的存储和加载操作重新排序 指令重新排序可能发生在编译器级别(甚至对MSIL)。方法调用如何改变编译器的工作方式? 例如,如果我这样做会发生什么: if (Random() > 0.5) Monitor.Enter(lockObj); 只有在运行时,当代码已经编译(到MSIL)并且似乎为时已晚时,才会决定是否需要内存屏障 我做错了什么?你的第一
lock
(意思是:Monitor.Enter
和Monitor.Exit
)在其周围添加了完整的内存围栏,以避免关键区域周围不必要的存储和加载操作重新排序
指令重新排序可能发生在编译器级别(甚至对MSIL)。方法调用如何改变编译器的工作方式?
例如,如果我这样做会发生什么:
if (Random() > 0.5)
Monitor.Enter(lockObj);
只有在运行时,当代码已经编译(到MSIL)并且似乎为时已晚时,才会决定是否需要内存屏障
我做错了什么?你的第一部分想法是正确的
lock(x){}
就是这个的缩写:
object __lockObj = x;
bool __lockWasTaken = false;
try
{
System.Threading.Monitor.Enter(__lockObj, ref __lockWasTaken);
// Your code...
}
finally
{
if (__lockWasTaken) System.Threading.Monitor.Exit(__lockObj);
}
资料来源:
但是,锁
对保护内存或告诉编译器对大括号内的代码执行不同的操作没有任何作用。在上面的链接中,您可以找到一个很好的小示例。这里是重要的部分这两种方法都使用lock(balanceLock)
因此,如果多个线程调用一种方法或另一种方法,则第一个线程将获得锁,并可以在作用域内执行代码。只要点击锁(balanceLock)
,其他线程就会被阻塞。如果第一个线程离开了lock(balanceLock)
的作用域,那么下一个线程可以获取锁并继续执行
public decimal Debit(decimal amount)
{
lock (balanceLock)
{
if (balance >= amount)
{
Console.WriteLine($"Balance before debit :{balance, 5}");
Console.WriteLine($"Amount to remove :{amount, 5}");
balance = balance - amount;
Console.WriteLine($"Balance after debit :{balance, 5}");
return amount;
}
else
{
return 0;
}
}
}
public void Credit(decimal amount)
{
lock (balanceLock)
{
Console.WriteLine($"Balance before credit:{balance, 5}");
Console.WriteLine($"Amount to add :{amount, 5}");
balance = balance + amount;
Console.WriteLine($"Balance after credit :{balance, 5}");
}
}
例如,如果您在公共无效信用证(十进制金额)
中移除锁(余额锁)
,则一切都会中断:
public decimal Debit(decimal amount)
{
lock (balanceLock)
{
if (balance >= amount)
{
Console.WriteLine($"Balance before debit :{balance, 5}");
Console.WriteLine($"Amount to remove :{amount, 5}");
balance = balance - amount;
Console.WriteLine($"Balance after debit :{balance, 5}");
return amount;
}
else
{
return 0;
}
}
}
public void Credit(decimal amount)
{
Console.WriteLine($"Balance before credit:{balance, 5}");
Console.WriteLine($"Amount to add :{amount, 5}");
balance = balance + amount;
Console.WriteLine($"Balance after credit :{balance, 5}");
}
下面逐步介绍在多线程环境中可能发生的情况:
- 线程#1调用
并获取锁公共十进制借方(十进制金额)
- 线程#2进行相同的调用,并在获取锁时被阻止,一切似乎都正常
- 但是线程#3调用
,并且可以操作公共无效信用(十进制金额)
,因为它从不检查是否有任何锁余额
- 线程#1也会操纵
,如果操作没有发生,则无法预测在线程#1和#3完成后平衡
的值平衡
,锁定
监视器
Mutex
联锁
如果您正在查找ProjectionBarrier功能,您将在
Interlocked
类中找到此功能:您的第一部分想法是正确的lock(x){}
就是这个的缩写:
object __lockObj = x;
bool __lockWasTaken = false;
try
{
System.Threading.Monitor.Enter(__lockObj, ref __lockWasTaken);
// Your code...
}
finally
{
if (__lockWasTaken) System.Threading.Monitor.Exit(__lockObj);
}
资料来源:
但是,锁
对保护内存或告诉编译器对大括号内的代码执行不同的操作没有任何作用。在上面的链接中,您可以找到一个很好的小示例。这里是重要的部分这两种方法都使用lock(balanceLock)
因此,如果多个线程调用一种方法或另一种方法,则第一个线程将获得锁,并可以在作用域内执行代码。只要点击锁(balanceLock)
,其他线程就会被阻塞。如果第一个线程离开了lock(balanceLock)
的作用域,那么下一个线程可以获取锁并继续执行
public decimal Debit(decimal amount)
{
lock (balanceLock)
{
if (balance >= amount)
{
Console.WriteLine($"Balance before debit :{balance, 5}");
Console.WriteLine($"Amount to remove :{amount, 5}");
balance = balance - amount;
Console.WriteLine($"Balance after debit :{balance, 5}");
return amount;
}
else
{
return 0;
}
}
}
public void Credit(decimal amount)
{
lock (balanceLock)
{
Console.WriteLine($"Balance before credit:{balance, 5}");
Console.WriteLine($"Amount to add :{amount, 5}");
balance = balance + amount;
Console.WriteLine($"Balance after credit :{balance, 5}");
}
}
例如,如果您在公共无效信用证(十进制金额)
中移除锁(余额锁)
,则一切都会中断:
public decimal Debit(decimal amount)
{
lock (balanceLock)
{
if (balance >= amount)
{
Console.WriteLine($"Balance before debit :{balance, 5}");
Console.WriteLine($"Amount to remove :{amount, 5}");
balance = balance - amount;
Console.WriteLine($"Balance after debit :{balance, 5}");
return amount;
}
else
{
return 0;
}
}
}
public void Credit(decimal amount)
{
Console.WriteLine($"Balance before credit:{balance, 5}");
Console.WriteLine($"Amount to add :{amount, 5}");
balance = balance + amount;
Console.WriteLine($"Balance after credit :{balance, 5}");
}
下面逐步介绍在多线程环境中可能发生的情况:
- 线程#1调用
并获取锁公共十进制借方(十进制金额)
- 线程#2进行相同的调用,并在获取锁时被阻止,一切似乎都正常
- 但是线程#3调用
,并且可以操作公共无效信用(十进制金额)
,因为它从不检查是否有任何锁余额
- 线程#1也会操纵
,如果操作没有发生,则无法预测在线程#1和#3完成后平衡
的值平衡
,锁定
监视器
Mutex
联锁
如果您正在查找ProjectionBarrier功能,您将在
Interlocated
类中找到它:考虑任何“内存屏障”都是监视器的一部分。输入操作-因此,如果未调用它,则运行时保证更改wrt。访问权限(即使这样的访问权限在之前没有发生冲突……)也就是说,如果代码始终不参与,那么在某些情况下,它可能不会产生正确的/预期的行为。(核心问题似乎很好,例如……可能不太多。)@user2864740我们中的一个不理解另一个。我并不打算实际使用上述代码,它只是一个示例。考虑下面的场景。代码>标志=真;Monitor.Exit();数字=3代码>。如果编译器不知道Monitor.Exit()应该是内存障碍,它可能会决定在标志=true
和number=3
之间切换。这会产生线程安全问题。在编译时,编译器应该如何不这样做?考虑任何“内存障碍”是<代码>监视器的一部分。输入< /代码>(或<代码>退出>代码>操作:如果执行了这样的代码路径,则只需要考虑/应用(读取:任何同步保证);在这种情况下必须这样做。见上文。编译器不需要做出“有条件的决定”。(一种不符合