ThreadSafety和静态字段 - 混乱?

时间:2014-05-22 07:56:01

标签: c# .net multithreading static field

我阅读了一些文章并进行了一些测试/调查,我认为这不是准确的推理(由于措辞不正确)

好的。很少有调查:

有人问this question

  

为什么静态字段通常被认为是线程安全的?

嗯,所有的回复者说:不,他们不是。

埃里克还说:

  

你有它倒退。因为静态字段和方法很可能   从多个线程访问,这是一个很好的编程实践   做这项工作以确保它们是线程安全的。

(这与我的说法非常接近,我稍后会在这里提出要求)

但我也发现汉斯说here

enter image description here

哦,男孩 - this other guy 说:

  但是,重新入侵仍然是一个问题。 static不是线程安全的,甚至静态只读也不是可重入的。

好的,我很困惑。

P.S。我谈论这种情况:

public class A
 {
  public static List<int> LST = new List<int>();
 }

List<> 线程安全,所以我不在乎谁拥有它。但是 - &gt;

我们来看看这个

public class A
 {
  public static int Five=5;
 }

什么对此代码不是线程安全的?

我看不出Five如何不能总是5:

enter image description here

据我所知 - Five永远不会是0(初始值),无论有多少线程访问它,它总是五个!

现在,我同意如果我正在编写使用此Five字段的代码,那么我必须注意更改的值!

示例:

if (Five < array.Length) return array[five]; //can throw an IndexOutOfBoundsException if five is modified

有了这个 - 我同意了!

但是(imho)不正确说(通常)静态字段不是线程安全的!

他们的用法必须被视为可以通过另一个线程进行更改。

(另外,显然 - 引用非线程安全对象的静态字段,如List<> - 肯定不会是线程安全的)

我在这里要说的是静态字段对于特定场景不是线程安全的,而不是一般规则!

问题:

  • 有人可以光明吗?我确定我在这里遗漏了一些东西(或者我不是?)静态字段不是总是非线程安全(通常)

  • 这个readonly如何使静态字段线程安全(它只是一次初始化而没有重新分配)?它是否使线程安全或不是? (那还有什么呢?)

6 个答案:

答案 0 :(得分:2)

一个静态变量,并不代表它的常量&#34;,它只是意味着它是在编译时静态分配的,而不是在运行时动态分配的。

如果在启动任何线程之前设置静态变量,然后永远不会再次更改它,但只能从中读取,那么,根据代码中逻辑的性质,您永远不会处于某种情况在这种情况下,您将面临与非线程安全代码相关的问题。这与静态变量是线程安全的说法不同。

调用先占线程,因为线程切换可以导致代码执行抢占另一个线程的执行,即使在中间写入或中间读取时也是如此。换句话说,请考虑您的静态变量是一个整数,取决于体系结构,可以是4个字节或8个字节。除非变量是专门声明的原子,否则一个线程可以开始读取内存,但是说它只能在另一个线程抢占它并写入同一个变量之前设法读取前2个字节。当第一个线程恢复时,它会读取剩余的字节,当然,到现在为止,它们是不一致的。

答案 1 :(得分:2)

public class A
{
  public static int Five=5;
}

这个例子在某种意义上不是线程安全的,如果两个线程执行return ++Five;,两个线程可能最终返回6,而不是返回6然后返回7.这是一个竞争条件。< / p>

这就是为什么Hans说只读字段本质上是线程安全的(有点)。他忘了提到的是,字段的类型应该是不可变的(int是)。

表示只能读取且永不改变的字段是线程安全的。

  

我在这里要说的是静态字段对于特定场景不是线程安全的,而不是一般规则!

是的,静态与线程安全无关。线程安全取决于:

  • 变量是否在不同的线程中共享
  • 该变量是否可以变异。
  • 这些突变是否是原子的。

答案 2 :(得分:1)

它确实使线程安全 - 但这是readonly的一个特性,而不是静态的。无法更改值的变量是每个定义,而不是在另一个线程中读取时可以在一个线程中进行更改的内容,因为......缺少开头的更改。

通常,任何只读的都是按照定义线程安全的。只有在价值发生变化时才会遇到麻烦。

答案 3 :(得分:1)

Readyonly静态字段只能在声明时或静态构造函数中初始化,而不能在之后初始化。因此,当访问该类的第一个静态字段或方法时,只能调用一次的静态构造函数不能改变它们。静态字段(非readonly)本身不是线程安全的,因为多个线程仍然可以访问它们并更改它们以产生竞争条件。

如果你没有改变一个类中的任何状态,那么就不存在使其线程安全的问题。线程安全是为了修改字段以避免竞争条件并保持它们在线程中的值保持一致。

请在控制台中运行以下示例以检查输出,以查看字段five不是线程安全的。

public class CheckThreadSafety
{
    private static int five = 5;

    public static void Increment(int iVal)
    {
        five += iVal;
    }

    public static int CurrentFiveValue { get { return five; } }
}

public class Program
{
    public static void Main()
    {
        for (var i=0; i<10000; i++)
        {   var t = i;
            Task.Factory.StartNew ( () =>
            {   
                CheckThreadSafety.Increment(t);
                Console.WriteLine("Five Value should be : " + (t + 5).ToString() + ", and is: " + CheckThreadSafety.CurrentFiveValue);
            } );
        }
    }
}
  

此外,如果您将上述代码中的five字段更改为公开,那么   呼叫者必须确保它的线程安全。

使用Singleton的另一个示例,阅读更多here

-Below代码线程安全:

using System;

public class Singleton
{
   private static Singleton instance;

   private Singleton() {}

   public static Singleton Instance
   {
      get 
      {
         if (instance == null)
         {
            instance = new Singleton();
         }
         return instance;
      }
   }
}

- 但是, readonly 引起的代码是线程安全的:

public sealed class Singleton
{       
    private static readonly Singleton instance = new Singleton();

    private Singleton() { }

    public static Singleton Instance
    {
        get 
        {
            return instance; 
        }
    }
}

答案 4 :(得分:1)

  

静态字段是不是总是非线程安全的?

static不应该在论证中。 static 没有与线程安全有关。它只允许字段成为“类型”本身的一部分而不是“实例”。

  

这个只能使静态字段线程安全吗?可以   使它线程安全或不是吗?

答案取决于what do you meant by thread safe?线程安全是一个更通用的术语,其中每个人都有自己的意思,如果你可以更精确地表达线程安全的意思,那么很容易回答。

答案 5 :(得分:1)

  1. 大于32位的字段(至少在32位系统上)甚至无法以原子方式访问。
  2. 与属性相比,字段(甚至非静态)不提供任何控制其访问的机制。
  3. 所以问题是,你真的需要将你的代码暴露给所有潜在的问题吗?在您的应用中添加public static字段,可以简单地从任意代码中更改它并创建线程问题。

    或者,您可以决定所有呼叫者在使用该字段之前必须锁定,其中锁定对象也是public static字段。因此,您将线程安全问题委托给该类的调用者,并且仅依赖于约定来保护您的代码安全。为什么,当有更好的方法来实现它?僵局怎么样?

    正如你自己说过的那样:

      

    他们的使用必须被视为可以通过另一个线程进行更改。

    我很确定这意味着你实际上意识到它们本质上不是线程安全的。