一个是包含线程安全的HashSet的< T> [英] Is Contains thread safe in HashSet<T>

查看:298
本文介绍了一个是包含线程安全的HashSet的< T>的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

寻找包含的HashSet< T> 类在.NET源代码,我找不到任何理由包含是不是线程安全的?

Looking at the code for Contains in the HashSet<T> class in the .NET source code, I cannot find any reason why Contains is not thread safe?

我加载 HashSet的< ; T> 与值的时间提前,然后检查包含在多线程进行AsParallel()循环。

I am loading a HashSet<T> with values ahead of time, and then checking Contains in a multi threaded .AsParallel() loop.

是否有任何理由,这不会是安全的。
我不愿意用 ConcurrentDictionary 时,我实际上并不需要存储的值。

Is there any reason why this would not be safe. I am loath to use ConcurrentDictionary when I don't actually require storing values.

推荐答案

的(正常),它仅用于读取集合是内定线程安全的(没有在.NET没有收藏,我知道读期间可修改本身)。还有一些注意事项:

Normally (normally) collections that are used only for reading are "unofficially" thread safe (there is no collection in .NET that I know that modifies itself during reading). There are some caveats:


  • 本身不能成为项目线程安全的(但有一个的HashSet< T> 这个问题应尽量减少,因为你不能从中提取项目。仍然是的GetHashCode()等于( )必须是线程安全的。如果,例如,他们访问加载点播懒惰的对象,他们可能是没有线程安全的,或者他们缓存/ memoize的一些数据来加速后续操作)

  • 您必须确保一个写操作后有一个 Thread.MemoryBarrier()(在同一个线程中完成作为写)或同等学历,否则在另一个线程读取可以读取的数据不完整

  • 您必须确保在做每一个线程(从一个,你做了一个写不同),前第一次读有一个 Thread.MemoryBarrier()。请注意,如果的HashSet< T> 是准备(与末尾的Thread.MemoryBarrier())创建/启动其他线程之前,那么 Thread.MemoryBarrier()是没有必要的,因为线程不能有内存的读取过时(因为它们不存在)。各种操作引起的隐式 Thread.MemoryBarrier()。例如,如果在那里之前, HashSet的<创建的线程; T> 弥漫,进入了一个等待()键,分别为未娇娇的HashSet< T> 填充(加上其 Thread.MemoryBarrier() ),在退出等待()导致一个隐含的 Thread.MemoryBarrier()

  • The items themselves could not be thread safe (but with an HashSet<T> this problem should be minimized, because you can't extract items from it. Still the GetHashCode() and the Equals() must be thread-safe. If, for example, they access lazy objects that are loaded on-demand, they could be not-thread safe, or perhaps they cache/memoize some data to speed-up subsequent operations)
  • You must be sure that after the last write there is a Thread.MemoryBarrier() (done in the same thread as the write) or equivalent, otherwise a read on another thread could read incomplete data
  • You must be sure that in each thread (different from the one where you did a write), before doing the first read there is a Thread.MemoryBarrier(). Note that if the HashSet<T> was "prepared" (with the Thread.MemoryBarrier() at the end) before creating/starting the other threads, then the Thread.MemoryBarrier() isn't necessary, because the threads can't have a stale read of the memory (because they didn't exist). Various operations cause an implicit Thread.MemoryBarrier(). For example if the threads where created before the HashSet<T> was filled, entered a Wait() and were un-Waited after the HashSet<T> was filled (plus its Thread.MemoryBarrier()), exiting a Wait() causes an implicit Thread.MemoryBarrier()

这是使用记忆化/延迟加载/无论你怎么称呼它,并以这种方式可以打破线程安全类的一个简单的例子。

A simple example of a class that uses memoization/lazy loading/whatever you want to call it and in that way can break the thread safety.

public class MyClass
{
    private long value2;

    public int Value1 { get; set; }

    // Value2 is lazily loaded in a very primitive
    // way (note that Lazy<T> *can* be used thread-safely!)
    public long Value2
    {
        get
        {
            if (value2 == 0)
            {
                // value2 is a long. If the .NET is running at 32 bits,
                // the assignment of a long (64 bits) isn't atomic :)
                value2 = LoadFromServer();

                // If thread1 checks and see value2 == 0 and loads it,
                // and then begin writing value2 = (value), but after
                // writing the first 32 bits of value2 we have that
                // thread2 reads value2, then thread2 will read an
                // "incomplete" data. If this "incomplete" data is == 0
                // then a second LoadFromServer() will be done. If the
                // operation was repeatable then there won't be any 
                // problem (other than time wasted). But if the 
                // operation isn't repeatable, or if the incomplete 
                // data that is read is != 0, then there will be a
                // problem (for example an exception if the operation 
                // wasn't repeatable, or different data if the operation
                // wasn't deterministic, or incomplete data if the read
                // was != 0)
            }

            return value2;
        }
    }

    private long LoadFromServer()
    {
        // This is a slow operation that justifies a lazy property
        return 1; 
    }

    public override int GetHashCode()
    {
        // The GetHashCode doesn't use Value2, because it
        // wants to be fast
        return Value1;
    }

    public override bool Equals(object obj)
    {
        MyClass obj2 = obj as MyClass;

        if (obj2 == null)
        {
            return false;
        }

        // The equality operator uses Value2, because it
        // wants to be correct.
        // Note that probably the HashSet<T> doesn't need to
        // use the Equals method on Add, if there are no
        // other objects with the same GetHashCode
        // (and surely, if the HashSet is empty and you Add a
        // single object, that object won't be compared with
        // anything, because there isn't anything to compare
        // it with! :-) )

        // Clearly the Equals is used by the Contains method
        // of the HashSet
        return Value1 == obj2.Value1 && Value2 == obj2.Value2;
    }
}

这篇关于一个是包含线程安全的HashSet的&LT; T&GT;的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

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