【问题标题】:Is volatile a good practice to avoid race conditions in C#?volatile 是避免 C# 中的竞争条件的好习惯吗?
【发布时间】:2018-04-04 12:13:32
【问题描述】:

我想知道在像这样的特定情况下,我们是否有 bool 标志来指示某些服务是否可用:

    private bool isAvailable;
    private void foo()
    {
        if(isAvailable)
        {
            isAvailable = false;
            DoSomething();
            isAvailable = true;
        }
    }

在多线程环境中将 bool 字段设置为 volatile 就足够了,还是使用锁甚至监视器更好。

在这种特定情况下,如果该服务目前不可用,则无需等待它再次可用。

【问题讨论】:

  • 该操作不是原子的,因此使 bool volatile 无济于事。

标签: c# .net multithreading thread-safety


【解决方案1】:

不,这还不够。 volatile 不足以管理多线程,实际上,volatile 可能会增加预期结果与实际结果之间的差距。访问变量 (isAvailable) 并对其进行修改仍然会导致竞争条件。 您可以使用锁、信号量或原子操作来管理多线程操作。

private object myLock = new object;
private void foo()
{
    lock(myLock)
    {
        DoSomething();
    }
}

[MethodImpl(MethodImplOptions.Synchronized)]
private void foo()
{
    // DoSomething
}

@Fildor 是对的。我错过了问题的概念,因此,我编辑了我的答案。下面的代码,如果资源没有被锁定,代码锁定资源并做一些事情,否则跳过临界区。

private object myLock = new object;
private void foo()
{
    if (Monitor.TryEnter(lockObject))
    {
        try
        {
            //do something
        }
        finally
        {
            Monitor.Exit(lockObject);
        }
    }
}

【讨论】:

  • 我从未见过使用过这个属性,你能解释一下吗?
  • 我感觉这些解决方案改变了 OP 的语义。它们都将阻塞,直到资源可用,然后对其执行操作。但是,如果资源可用,OP 只需要执行操作。如果不是:不在乎。
  • Synchronized 将整个方法封装在一个锁中。如果您想阅读和了解更多信息(有替代锁管理),您可以查看此链接:bengribaudo.com/blog/2013/04/16/2237/…
【解决方案2】:

其他答案可为您的问题提供解决方案,但以防万一您想知道为什么您的解决方案不起作用...

...以下是可能发生的情况:

Thread A                                Thread B
--------                                --------
fetches isAvailable from memory
Tests the fetched value, it's true!     fetches isAvailable from memory
sets isAvailable=false                  Tests the fetched value, it's true!
calls DoSomething()                     sets isAvailable=false
...                                     calls DoSomething()
                                        ...

【讨论】:

  • 有道理!看来 Monitor.TryEnter(lock) 是解决这个问题的最简单方法。
【解决方案3】:

Adem Catamak 完全正确,locks 通常是要走的路。

但是,如果您的代码对性能非常关键,您可以看看使用 MemoryBarriervolatile

此页面在这方面非常有帮助:http://www.albahari.com/threading/part4.aspx

要在lock 答案中添加一些内容,您可以随时将其抽象出来(未经测试,但您明白了要点:

public class ThreadSafeObject
{
    private object _myObject; //Must be reference type
    public object MyObject
    {
        get
        {
            lock (_myObject)
            {
                return _myObject;
            }

        }
        set
        {
            lock (_myObject)
            {
                _myObject = value;
            }
        }
    }
}

您甚至可以使用泛型来实现:

public class ThreadSafeGeneric<T>
{
    private T _myObject;
    public T MyObject
    {
        get
        {
            lock (_myObject)
            {
                return _myObject;
            }

        }
        set
        {
            lock (_myObject)
            {
                _myObject = value;
            }
        }
    }
}

【讨论】:

  • 否决票的任何理由?如果我的回答实际上有问题,我会删除它。
最近更新 更多