Interlocked 是否保证对 C# 中其他线程的可见性,还是我仍然必须使用 volatile?

发布于 2024-08-25 22:58:08 字数 1617 浏览 5 评论 0原文

我一直在阅读 类似的问题,但我还是有点困惑......阿贝尔有一个很好的答案,但这是我不确定的部分:

...声明一个变量 volatile 使得它对于每个单独的变量都是 不稳定的 使用权。这是不可能强迫的 任何其他方式的行为,因此不稳定 不能用互锁代替。 这在以下场景中是需要的 其他库、接口或 硬件可以访问您的变量并且 随时更新,或者最需要的 最新版本。

Interlocked 是否保证原子操作对所有线程的可见性,或者我是否仍然必须在值上使用 volatile 关键字以保证更改的可见性?

这是我的示例:

volatile int value = 100000; // <-- do I need the volitile keyword
// ....

public void AnotherThreadMethod()
{
 while(Interlocked.Decrement(ref value)>0)
 {
  // do something
 }
}


public void AThreadMethod()
{
 while(value > 0)
 {
  // do something
 }
}

更新:
我是一个糟糕的运动,我改变了原来的例子,所以这里又是:

public class CountDownLatch
{
    private volatile int m_remain; // <--- do I need the volatile keyword here?
    private EventWaitHandle m_event;

    public CountDownLatch(int count)
    {
        Reset(count);
    }

    public void Reset(int count)
    {
        if (count < 0)
            throw new ArgumentOutOfRangeException();
        m_remain = count;
        m_event = new ManualResetEvent(false);
        if (m_remain == 0)
        {
            m_event.Set();
        }
    }

    public void Signal()
    {
        // The last thread to signal also sets the event.
        if (Interlocked.Decrement(ref m_remain) == 0)
            m_event.Set();
    }

    public void Wait()
    {
        m_event.WaitOne();
    }
}

I've been reading the answer to a similar question, but I'm still a little confused... Abel had a great answer, but this is the part that I'm unsure about:

...declaring a variable volatile makes it volatile for every single
access. It is impossible to force this
behavior any other way, hence volatile
cannot be replaced with Interlocked.
This is needed in scenarios where
other libraries, interfaces or
hardware can access your variable and
update it anytime, or need the most
recent version.

Does Interlocked guarantee visibility of the atomic operation to all threads, or do I still have to use the volatile keyword on the value in order to guarantee visibility of the change?

Here is my example:

volatile int value = 100000; // <-- do I need the volitile keyword
// ....

public void AnotherThreadMethod()
{
 while(Interlocked.Decrement(ref value)>0)
 {
  // do something
 }
}


public void AThreadMethod()
{
 while(value > 0)
 {
  // do something
 }
}

Update:
I was a bad sport and I changed the original example, so here it is again:

public class CountDownLatch
{
    private volatile int m_remain; // <--- do I need the volatile keyword here?
    private EventWaitHandle m_event;

    public CountDownLatch(int count)
    {
        Reset(count);
    }

    public void Reset(int count)
    {
        if (count < 0)
            throw new ArgumentOutOfRangeException();
        m_remain = count;
        m_event = new ManualResetEvent(false);
        if (m_remain == 0)
        {
            m_event.Set();
        }
    }

    public void Signal()
    {
        // The last thread to signal also sets the event.
        if (Interlocked.Decrement(ref m_remain) == 0)
            m_event.Set();
    }

    public void Wait()
    {
        m_event.WaitOne();
    }
}

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(2

神经大条 2024-09-01 22:58:08

它们不需要**波动性,因为您从不检查互锁变量的值。相反,您始终检查联锁操作返回的值。混合互锁操作和普通赋值/比较总是会导致错误的代码。

我不确定 Reset() 函数的意图是什么,但是这段代码在线程间原语中没有位置:您分配给 m_remain,直接检查 m_remain 的值,这是非常糟糕的。我强烈建议您将其删除:不仅实施不正确,而且我非常怀疑是否需要“重置”计数器中年的语义。保持简单:ctor(将代码从 Reset 移入其中) Signal 和 Wait 是唯一需要的三个运算符,并且它们现在是正确的。

已更新 编辑代码后。

忽略你不应该混合两者的事实,如果你最终混合了它们那么是的,仍然需要 volatility。 Volatile 主要涉及 IL 代码和生成的 JIT 代码,以确保始终从实际内存位置读取值,并且不会发生优化,例如代码重新排序。事实上,一段不相关的代码使用互锁操作更新值对读取该值的其他部分没有影响。如果没有 volatile 属性,编译器/JIT 仍可能生成忽略其他地方发生的写入的代码,如果写入是互锁的或直接赋值,则无关紧要。

顺便说一句,有一些混合普通读取和互锁操作的有效模式,但它们通常涉及 Interlocked.CompareExchange 和这样的 go:读取当前状态,基于当前状态进行一些计算,尝试将状态替换为互锁比较交换:如果成功则很好,如果不成功则丢弃计算结果并返回步骤1。

Them do not *need** volatility, because you never check the value of the interlocked variable. Instead you always check the value returned by the interlocked operation(s). Mixing interlocked operations and ordinary assignment/comparison always result in incorrect code.

I'm not sure what the Reset() function intent is, but that piece of code has no place in inter-thread primitive: you assign to m_remain, you check the value of m_remain directly, is pretty bad. I strongly suggest you take it out: not only is implemented incorrectly, but I highly doubt the semantics of 'resetting' the counter mid-life-span are needed. Leave it simple: ctor (move the code from Reset into it) Signal and Wait are the only three operators needed, and they are correct as they are now.

Updated After you edited the code.

Ignoring the fact that you shouldn't mix the two, if you do end up mixing them then yes, volatile is still needed. Volatile is primarily about the IL code and the JIT code generated to make sure the value is always read from the actual memory location and no optimization occurs, like code reordering. The fact that an unrelated piece of code updates the value using Interlocked operations play no effect on other parts that read the value. W/o a volatile attribute, the compiler/JIT may still generate code that ignores the writes that occur somewhere else, irrelevant if the writes are interlocked or direct assignment.

BTW, there are valid patterns that mix ordinary read and interlocked operations, but they usually involve Interlocked.CompareExchange and the go like this: read current state, do some computation based on current state, attempt to replace state as an interlocked compare-exchange: if succeed fine, if not drop the computation result and go back to step 1.

装迷糊 2024-09-01 22:58:08

我认为 System.Threading.Thread.VolatileRead(ref myVariable) 可能就是您正在寻找的。与 Interlocked.Increment 结合使用,它可用于保证更改是原子的,并且您读取的值是最新的。

I think System.Threading.Thread.VolatileRead(ref myVariable) might be what you are looking for. Used in conjunction with Interlocked.Increment it can be used to guarantee that changes are atomic and the values that you read are the very latest.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文