创建对象线程新实例的静态方法安全吗?

时间:2019-08-07 22:29:21

标签: c# parallel-processing selenium-grid

根据我在其他SO文章上所读的内容,我的猜测是CreateTry1和CreateTry2都不是线程安全的,但是即使阅读了这么多内容,我仍然不确定100%这是正确的。

public ClassA
{
  private string MyString;

  private ClassA(string myString)
  {
    MyString = myString;
  }

  public static CreateTry1(string aString)
  {
    return new ClassA(aString);
  }

  public static CreateTry2(string aString)
  {
    ClassA a = new ClassA();
    a.MyString = aString;
    return a;
}

// The Below is not the main focus of the question
static public void Main(String[] args)
{
  Thread t1 = new Thread(p =>
  {
    int i = 0;
    while(i < 1000000)
    {
      ClassA newA = ClassA.CreateTry2("Hello");
      i++;
    }
  });

  Thread t2 = new Thread(p =>
  {
    int i = 0;
    while(i < 1000000)
    {
      ClassA newA = ClassA.CreateTry2("Hello");
      i++;
    }
  });

  t1.Start();
  t2.Start();
}

更多信息1:澄清

我编辑了密码

此问题专门与Selenium Grid有关。我以为,如果我对线程安全(通常不是线程安全)有一个更好的了解,那么在重构代码以在Selenium Grid中并行运行时,我将知道如何避免不安全的代码。但是,我想我跳入游泳池的深处,不完全知道如何在这里游泳,因此深表歉意。

我认为答案的一部分可能很简单,例如“非静态类中的非静态变量(字段和属性)始终是线程安全的”。静态变量(字段和属性) )在非静态或静态类中通常是不安全的。


更多信息2:相关的SO帖子

我读了很多帖子。有些陈述使事情变得清晰,而另一些则使事情更加混乱。

This comment made most sense。但是,该语句的一部分说:“当单独实例中的数据仍然混杂在一起时,最有可能是因为数据不是真正分开的。”似乎很难不看到您的一个变量(字段)是静态的,所以想知道如果没有他的了解,线程间数据的这种交叉污染怎么可能发生。

任何与以下MSDN语句相关的对话对我来说都不是完全有意义:

  

此类型的任何公共静态(在Visual Basic中为Shared)成员都是线程安全的。不保证任何实例成员都是线程安全的。

1. About above quote

2. About above quote。这是一个很好的问题,但最初的答案似乎过于复杂。我最终记得“成员”是指类中的字段,属性和方法(而不仅仅是字段)。如此广泛的陈述会使答案变得复杂。

以下帖子"Is this extension method thread safe"是一个很好的问题,但我不知道哪个答案是正确的。我相信这两个姿势都是正确的,但是Avner的回答很笼统,Ondrej的回答专门针对了这个问题。

一些帖子like this one提出了“共享状态”一词,after reading the following似乎不过是类中的静态字段(但我很容易错了)。

2 个答案:

答案 0 :(得分:1)

我不确定您要找出什么,但我想知道两件事可能会有所帮助:

  • CreateTry1CreateTry2无法访问共享数据。 MyString属性未在实例之间共享。因此,这些方法是线程安全的。
  • 保证构造函数是线程安全的。并行ClassA的多个实例化不会相互干扰,即是线程安全的。

答案 1 :(得分:1)

  

创建对象线程的新实例的静态方法安全吗?

不。这不是线程安全的充分必要条件。

以此为您的问题的反例:

public class Foo
{
    public int Value;
    private Foo()
    {
        this.Value = magic;
    }
    private static int magic = 42;
    public static Foo CreateFooIncrement()
    {
        magic += 1;
        return new Foo();
    }
    public static Foo CreateFooDecrement()
    {
        magic -= 1;
        return new Foo();
    }   
}

基于对FooValue的并发调用,肯定有可能使用意外的CreateFooIncrement创建CreateFooDecrement实例。

此示例类Foo具有创建其自己类的实例的静态方法,并且绝对不是线程安全的。