简体   繁体   English

C#无锁编码完整性检查

[英]C# lock free coding sanity check

UPDATED : now using a read-only collection based on comments below 更新 :现在使用基于以下注释的只读集合

I believe that the following code should be thread safe "lock free" code, but want to make sure I'm not missing something... 我相信以下代码应该是线程安全的“无锁”代码,但要确保我没有丢失任何东西...

public class ViewModel : INotifyPropertyChanged
{
   //INotifyPropertyChanged and other boring stuff goes here...

   private volatile List<string> _data;
   public IEnumerable<string> Data
   {
      get { return _data; }
   }

   //this function is called on a timer and runs on a background thread
   private void RefreshData()
   {
      List<string> newData = ACallToAService();
      _data = newData.AsReadOnly();
      OnPropertyChanged("Data"); // yes, this dispatches the to UI thread
   }
}

Specifically, I know that I could use a lock(_lock) or even an Interlocked.Exchange() but I don't believe that there is a need for it in this case. 具体来说,我知道我可以使用lock(_lock)甚至是Interlocked.Exchange()但是我不认为在这种情况下需要使用它。 The volatile keyword should be sufficient (to make sure the value isn't cached), no? volatile关键字应该足够了(以确保不缓存该值),不是吗? Can someone please confirm this, or else let me know what I don't understand about threading :) 有人可以确认一下,还是让我知道我对线程不了解的内容:)

I have no idea whether that is "safe" or not; 我不知道那是否“安全”。 it depends on precisely what you mean by "safe". 这完全取决于您所指的“安全”。 For example, if you define "safe" as "a consistent ordering of all volatile writes is guaranteed to be observed from all threads", then your program is not guaranteed to be "safe" on all hardware. 例如,如果将“安全”定义为“保证从所有线程都能观察到所有易失性写入的一致顺序”,那么就不能保证您的程序在所有硬件上都是“安全”的。

The best practice here is to use a lock unless you have an extremely good reason not to. 此处的最佳做法是使用锁,除非您有非常充分的理由不这样做。 What is your extremely good reason to write this risky code? 您编写此危险代码的极好的理由是什么?

UPDATE: My point is that low-lock or no-lock code is extremely risky and that only a small number of people in the world actually understand it. 更新:我的观点是,低锁或无锁代码极具风险,并且世界上只有很少一部分人真正理解它。 Let me give you an example, from Joe Duffy: 让我举一个乔·达菲的例子:

// deeply broken, do not use!
class Singleton {
    private static object slock = new object();
    private static Singleton instance;
    private static bool initialized;
    private Singleton() {}
    public Instance {
        get {
            if (!initialized) {
                lock (slock) {
                    if (!initialized) {
                        instance = new Singleton();
                        initialized = true;
                    }
                }
            }
            return instance;
        }
    }
}

This code is broken; 该代码已损坏; it is perfectly legal for a correct implementation of the C# compiler to write you a program that returns null for the instance. 对于C#编译器的正确实现,为您编写一个为实例返回null的程序是完全合法的。 Can you see how ? 你看到了吗? If not, then you have no business doing low-lock or no-lock programming; 如果没有,那么您就没有业务进行低锁或无锁编程。 you will get it wrong. 弄错的。

I can't figure out this stuff myself; 我本人无法弄清楚这些东西。 it breaks my brain. 它伤了我的大脑。 That's why I try to never do low-lock programming that departs in any way from standard practices that have been analyzed by experts. 这就是为什么我尝试从不进行与专家分析过的标准实践完全不同的低锁编程的原因。

It depends on what the intent is. 这取决于意图。 The get/set of the list is atomic (even without volatile) and non-cached (volatile), but callers can mutate the list, which is not guaranteed thread-safe. 列表的获取/设置是原子的(即使没有volatile)和非缓存的(volatile),但是调用者可以更改列表,这不能保证线程安全。

There is also a race condition that could lose data: 还有一种竞争条件可能会丢失数据:

 obj.Data.Add(value);

Here value could easily be discarded. 这里的值很容易被丢弃。

I would use an immutable (read-only) collection. 我会使用一个不可变的(只读)集合。

I believe that this is safe in itself (even without volatile), however there may be issues depending on how other threads use the Data property. 我相信这本身是安全的(即使没有volatile), 但是根据其他线程如何使用Data属性,可能会出现问题。

Provided that you can guarantee that all other threads read and cache the value of Data once before doing enumeration on it (and don't try to cast it to some broader interface to perform other operations), and make no consistency assumptions for a second access to the property, then you should be ok. 前提是您可以保证所有其他线程在进行枚举之前都读取并缓存一次Data的值(并且不要尝试将其强制转换为更广泛的接口以执行其他操作), 并且不对第二次访问进行一致性假设到物业,那你应该没事。 If you can't make that guarantee (and it'd be hard to make that guarantee if eg. one of the users is the framework itself via data-binding, and hence code that you do not control), then you can't say that it's safe. 如果您不能做出保证(并且例如,如果一个用户是通过数据绑定的框架本身,因此是您无法控制的代码,那么就很难做出保证),那么您就无法说这很安全。

For example, this would be safe: 例如,这将是安全的:

foreach (var item in x.Data)
{
   // do something with item
}

And this would be safe (provided that the JIT isn't allowed to optimise away the local, which I think is the case): 这是安全的(前提是不允许JIT优化本地,我认为是这样):

var data = x.Data;
var item1 = FindItem(data, a);
var item2 = FindItem(data, b);
DoSomething(item1, item2);

The above two might act on stale data, but it will always be consistent data. 上面的两个可能对过时的数据起作用,但是它将始终是一致的数据。 But this would not necessarily be safe: 但这不一定是安全的:

var item1 = FindItem(x.Data, a);
var item2 = FindItem(x.Data, b);
DoSomething(item1, item2);

This one could possibly be searching two different states of the collection (before and after some thread replaces it), so it may not be safe to operate on items found in each separate enumeration, as they may not be consistent with each other. 这可能正在搜索集合的两个不同状态(在某个线程替换它之前和之后),因此对每个单独的枚举中找到的项目进行操作可能并不安全,因为它们可能彼此不一致。

The issue would be worse with a broader interface; 随着界面的扩大,问题将变得更加严重。 eg. 例如。 if Data exposed IList<T> you'd have to watch for consistency of Count and indexer operations as well. 如果数据公开IList<T> ,则还必须注意Count和indexer操作的一致性。

I think that if you have only two threads like you described, your code is correct and safe. 我认为,如果只有两个您描述的线程,那么您的代码是正确且安全的。 And also you don't need that volatile, it is useless here. 而且您也不需要那种挥发性,在这里它是没有用的。

But please don't call it "thread safe", as it is safe only for your two threads using it your special way. 但是请不要称其为“线程安全”,因为只有您的两个线程以特殊方式使用它才安全。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM