是否互锁保证知名度,在C#中的其他线程或者我仍然要使用volatile? [英] Does Interlocked guarantee visibility to other threads in C# or do I still have to use volatile?

查看:145
本文介绍了是否互锁保证知名度,在C#中的其他线程或者我仍然要使用volatile?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我一直在阅读的答案,的类似的问题,但我还是有点迷茫......亚伯有一个伟大的答案,但是这是我不确定的部分:




...声明一个变量波动使得它的挥发性为每一个
访问。这是不可能的强制此
行为的任何其他方式,因此,挥发性
不能与联锁取代。
这是需要在场景中
其他库,接口或
的硬件可以访问您的变量,
随时更新,还是需要最
最新版本。




确实互锁的原子操作的保证可视性所有线程,还是我仍然必须以保证变更的知名度使用挥发性关键字的值?



下面是我的榜样

 挥发性int值= 100000; //<  - 我还需要volitile关键字
// ...

公共无效AnotherThreadMethod()
{
,而(Interlocked.Decrement(参考值)0)
{
//做些什么
}
}


公共无效AThreadMethod()
{
,而(值大于0)
{
//做些什么
}
}

更新:结果
我是一个糟糕的运动,我改变了原来的例子,所以这里要再次重申:

 公共类CountDownLatch 
{
私人挥发性INT m_remain; //< ---我需要volatile关键字吗?
私人的EventWaitHandle m_event;

公共CountDownLatch(诠释计数)
{
复位(计数);
}

公共无效复位(诠释计数)
{
如果(计数℃下)
抛出新ArgumentOutOfRangeException();
m_remain =计数;
m_event =新的ManualResetEvent(假);
如果(m_remain == 0)
{
m_event.Set();
}
}

公共无效信号()
{
//最后一个线程也发出信号设置事件。
如果(Interlocked.Decrement(REF m_remain)== 0)
m_event.Set();
}

公共无效等待()
{
m_event.WaitOne();
}
}


解决方案

他们不要的需求的*波动性,因为你的从不检查互锁变量的值。相反,你经常检查值的返回由联锁操作(S)。混合联锁操作和普通的分配/比较总是导致不正确的代码。



我不知道的Reset()函数的意图是什么,但是这段代码没有放在线程间原始:分配给m_remain,你直接检查m_remain的价值,是非常糟糕的。我强烈建议你把它拿出来:不仅实现不正确的,但我很怀疑复位计数器需要中等寿命的语义。把它简单:构造函数(摆脱复位到它的代码)信号和等待是唯一的三家运营商需要的,他们是因为他们现在是正确



更新在编辑的代码。



忽略了一个事实,你不应该将两者搅和,如果你最终将其混合然后是,挥发性仍然是必要的。挥发性主要是关于IL代码和生成,以确保该值总是从实际的内存位置读取JIT代码并没有出现优化,如代码重新排序。这无关一段代码使用互锁操作播放上读出的值其它部分没有效果更新价值的事实。 W / OA 挥发性属性,编译器/ JIT仍可能生成代码,无视发生在其他地方的写入,不着边际的话写操作互锁或直接分配。



BTW,有普通混合读取和联锁操作模式有效,但它们通常涉及Interlocked.CompareExchange和是这样的:读取当前的状态,做基于目前的状态一些计算,尝试更换状态互锁比较交换:如果成功罚款,如果不降的计算结果,并回到步骤1


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();
    }
}

解决方案

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.

这篇关于是否互锁保证知名度,在C#中的其他线程或者我仍然要使用volatile?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆