【问题标题】:Are static methods that creates a new instance of it's object thread safe?创建对象线程新实例的静态方法是否安全?
【发布时间】:2019-08-08 21:19:50
【问题描述】:

根据我在其他 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 中为共享)成员都是线程安全的。不保证任何实例成员都是线程安全的。

1. About above quote

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

下面的帖子"Is this extension method thread safe" 是一个很好的问题,但我不知道哪个答案是正确的。我相信这两种方法都是正确的,但 Avner 的答案是一个非常笼统的答案,而 Ondrej 的答案则专门回答了这个问题。

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

【问题讨论】:

  • 输入是不可变的,在方法返回之前没有其他线程可以知道正在创建的实例。是什么让您认为它们不是线程安全的?请编辑您的问题以包括对这些 SO 帖子的引用以及您得出结论的理由。没有这些背景信息很难回答这个问题。
  • 您的问题不清楚。您的代码示例中只有一个线程,因此一切都以简单、退化的方式是线程安全的。除了对上一条评论的正确分析之外,甚至不清楚您在这里寻找什么样的“线程安全”。

标签: c# parallel-processing selenium-grid


【解决方案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();
    }   
}

Foo 的实例当然有可能是基于对 CreateFooIncrementCreateFooDecrement 的并发调用而使用意外的 Value 创建的。

这个示例类Foo 具有创建自己类的实例的静态方法,它绝对不是线程安全的。

【讨论】:

  • 感谢您的帖子。帮了大忙。
【解决方案2】:

我不确定你想弄清楚什么,但我想知道两件事可能会有所帮助:

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

【讨论】:

  • 构造函数本身是线程安全的,但你当然可以在构造函数中编写非线程安全的代码。里面的代码不会神奇地变成线程安全的,不管这个词在这个上下文中是什么意思。
  • @LasseVågsætherKarlsen 是的,100% 正确。 通常,实例属性是在构造函数中访问的,并且保证是线程安全的。这个问题就是这种情况。
  • post 和 subcmets 帮了大忙。谢谢。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2018-02-09
  • 1970-01-01
  • 2017-07-26
相关资源
最近更新 更多