Jim*_*Jim 12 c# multithreading hashset
看一下.NET源代码Contains中的HashSet<T>类代码,我找不到任何Contains线程安全的原因?
我HashSet<T>提前加载了一个值,然后检查Contains多线程.AsParallel()环.
这有什么理由不安全吗?ConcurrentDictionary当我实际上不需要存储值时,我不愿意使用.
xan*_*tos 13
通常(通常)仅用于读取的集合是"非正式"线程安全的(.NET中没有我知道在读取期间自行修改的集合).有一些警告:
HashSet<T>这个问题应尽量减少,因为你不能从中提取项目.仍然是GetHashCode()和Equals()必须是线程安全的.如果,例如,他们访问所加载懒对象按需,它们可能不是线程安全的,或者它们可能缓存/记忆一些数据以加速后续操作)Thread.MemoryBarrier()(在与写入相同的线程中完成)或等效的,否则在另一个线程上读取可能会读取不完整的数据Thread.MemoryBarrier().请注意,如果HashSet<T>在创建/启动其他线程之前已经"准备好"(最后使用Thread.MemoryBarrier()),那么Thread.MemoryBarrier()就没有必要了,因为线程不能过时读取内存(因为他们不存在).各种操作导致隐含Thread.MemoryBarrier().例如,如果在HashSet<T>填充之前创建的线程,输入a Wait()并且un-Waited在HashSet<T>填充之后(加上它Thread.MemoryBarrier()),则退出a Wait()会导致隐式Thread.MemoryBarrier()使用memoization/lazy loading /任何你想要调用它的类的简单示例可以打破线程的安全性.
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;
    }
}
既然你用的时间提前值装入你的设置,你可以使用ImmutableHashSet<T>从System.Collections.Immutable库。在一成不变的馆藏宣传自己是线程安全的,所以我们没有约的“非官方”线程安全的担心HashSet<T>。
var builder = ImmutableHashSet.CreateBuilder<string>(); // The builder is not thread safe
builder.Add("value1");
builder.Add("value2");
ImmutableHashSet<string> set = builder.ToImmutable();
...
if (set.Contains("value1")) // Thread safe operation
{
 ...
}