Warning: file_get_contents(/data/phpspider/zhask/data//catemap/6/multithreading/4.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
C# 获取和设置简单的静态属性是线程安全的吗?_C#_Multithreading_Static_Thread Safety - Fatal编程技术网

C# 获取和设置简单的静态属性是线程安全的吗?

C# 获取和设置简单的静态属性是线程安全的吗?,c#,multithreading,static,thread-safety,C#,Multithreading,Static,Thread Safety,可能重复: 在下面的示例类中 static class Shared { public static string[] Values { get; set; } } 许多读卡器线程周期性地读取值字符串数组,而有时一个写入程序会使用setter将整个数组替换为一个新值。我需要使用读写器锁还是C#会自动处理 编辑:在我的例子中,唯一需要的“线程安全”是:当写入程序在读卡器搜索值时替换数组时,不应该发生任何错误。我不在乎读者是否会立即使用新值,只要他们将来会使用它这不是线程安全的。是的,您

可能重复:

在下面的示例类中

static class Shared
{
    public static string[] Values { get; set; }
}
许多读卡器线程周期性地读取
字符串数组,而有时一个写入程序会使用setter将整个数组替换为一个新值。我需要使用
读写器锁
还是C#会自动处理


编辑:在我的例子中,唯一需要的“线程安全”是:当写入程序在读卡器搜索值时替换数组时,不应该发生任何错误。我不在乎读者是否会立即使用新值,只要他们将来会使用它

这不是线程安全的。是的,您需要使用锁。

这取决于您所说的线程安全

读取和替换保证是原子的,但不能保证写入之后的读取必然会读取新值

响应您的编辑…


您现有的代码不会发生任何不好的事情(例如,撕碎的读取),但是不能保证您的读者会看到新的值。例如,旧引用有可能(尽管可能不太可能)永远缓存在寄存器中。

如果没有任何额外的保护,无法保证读取线程会看到新值。实际上,只要阅读线程正在做任何有意义的事情,它就会看到新的值。特别是,您永远不会看到“半”个更新,引用指向死区


如果将字段
设置为volatile
,我相信即使这种危险也会消除,但是无锁编程通常很难解释。当然,这意味着放弃自动实现的属性。

我会锁定它。对于多次读取,偶尔写入使用a-比


此用途是线程安全的:

string[] localValues = Shared.Values;
for (int index = 0; index < localValues.length; index++)
    ProcessValues(localValues[index]);
当然,用户仍然可以将GetValues()放在循环中,这同样糟糕,但至少显然是糟糕的

根据具体情况,更好的解决方案可能是分发副本,这样调用代码就根本不能改变数组。这是我通常会做的,但在你的情况下可能不合适

static class Shared
{
    private static string[] values;
    public static string[] GetValues()
    {
        string[] currentValues = values;
        if (currentValues != null)
            return (string[])currentValues.Clone();
        else
            return null;
    }
    public static void SetValues(string[] values)
    {
        Shared.values = values;
    }
}

为了稍微偏离主题,Java 2 1.5内存模型增加了两个保证(请参阅):

  • 易失性读/写也是内存障碍
  • final
    字段在构造函数外部完全初始化
后者是一个有趣的例子:您过去能够执行
foo=newstring(newchar[]{'a','b','c'})在一个线程中,并且
foo=“123”;System.out.println(foo)
在另一个线程中打印空字符串,因为无法保证写入foo的最终字段会在写入foo之前发生


我不确定.NET阵列初始化的细节;在这种情况下,您可能需要使用Thread.MemoryBarrier()(在getter的开头和setter的结尾)来确保读者只看到完全初始化的数组。

重复的?如果您在.NET 4中,请查看
System.Collections.Concurrent
命名空间。根据您的使用情况,它们可能也适合您的需要。我用所需的线程安全级别更新了我的问题。@LukeH:可能。。。我的理解也是有限的。但是,我相信它“或多或少”确保您始终读取最后一个值。(这甚至是MSDN对它的描述。)如果这个答案包含一些细节,比如你预见到的危险,“线程安全”和“非线程安全”使用得太过松散,那么这个答案将更加有用(或可伪造)。在这种情况下,读和写保证是原子的,但可能没有顺序。锁保留排序,但排序在这里可能并不重要。补充一点:在延迟读取场景中不需要锁,因为引用共享数组作为原子操作。从共享变量读取引用(指针)是线程安全的,即使另一个线程即将向共享变量写入新值。这是一种“分代”版本控制形式-每次引用共享数据时,它本质上是数据的一个不可变快照,在所有引用被释放之前保持活动状态。说明:当内存地址与dword对齐时,x86和x64上的引用指针读取在硬件级别是原子的,我很确定这是由.NET分配器保证的。非对齐读取可能需要多个时钟周期来读取每一半并合并它们,这为另一个线程中的写操作创造了一个机会窗口,以便在读操作读取下半部分之前更改内存中的值。@dthorpe-重要的是,C语言定义保证了对对象的引用将以原子形式复制,而不管它在哪个平台上运行,也不管它是如何运行的该机制已实现。在这种情况下,这是一件非常有用的事情。:-)是的,语言定义所做的断言实际上是由硬件实现支持的,这很好PBig无锁螺纹配合风扇。但愿我能多加一点你的答案。一个普通的
锁会更快。原因是RW锁有一组有限的场景,实际上它们在实践中更快。这不是其中之一。我刚刚在我的机器上做了一个基准测试,
大约快了5倍。我同意Brian,锁更快,但如果我们讨论的是大量线程同时访问数据,那么这可能有一些优势。但从人类的角度来看,这种差异是微不足道的!
for (int index = 0; index < Shared.Values.Length; index++)
    ProcessValues(Shared.Values[index]);
static class Shared   
{
    private static string[] values;   
    public static string[] GetValues() { return values; }
    public static void SetValues(string[] values) { Shared.values = values; }
}
static class Shared
{
    private static string[] values;
    public static string[] GetValues()
    {
        string[] currentValues = values;
        if (currentValues != null)
            return (string[])currentValues.Clone();
        else
            return null;
    }
    public static void SetValues(string[] values)
    {
        Shared.values = values;
    }
}