Warning: file_get_contents(/data/phpspider/zhask/data//catemap/2/csharp/320.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181

Warning: file_get_contents(/data/phpspider/zhask/data//catemap/6/multithreading/4.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
C# 像Thread.MemoryBarrier(或Monitor.Enter)这样的方法调用如何改变编译器的工作方式?_C#_Multithreading_Compilation - Fatal编程技术网

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
之间切换。这会产生线程安全问题。在编译时,编译器应该如何不这样做?考虑任何“内存障碍”是<代码>监视器的一部分。输入< /代码>(或<代码>退出>代码>操作:如果执行了这样的代码路径,则只需要考虑/应用(读取:任何同步保证);在这种情况下必须这样做。见上文。编译器不需要做出“有条件的决定”。(一种不符合