【问题标题】:Static or Non Static methods, thread safety is for the types not methods静态或非静态方法,线程安全是针对类型而不是方法
【发布时间】:2014-09-26 17:43:23
【问题描述】:

我有一段时间有这种困惑,静态方法实现线程是否安全,实例方法当然是线程安全的,如果我们为每个线程分配一个单独的实例,那么它们就不会干预,那么我意识到,线程安全更重要关于类型然后是方法,它们本身并不是内存分配,所以我们举个例子:

private static ConcurrentDictionary<int,int> cd;

public static void Method1(int userid)
{
   // Modify static object cd based on userid key
}

public void Method2(int userid)
{
    // Modify static object cd based on userid key
}

本质上,当运行时提供不同用户 ID 的多个线程访问时,两种方法之间没有区别。我已经测试过,但想验证我的理解是否正确。

【问题讨论】:

  • 当你投反对票时,请解释原因,问题有什么问题。每个人都不必有同样的疑问。停止这种无声的投票,至少说出你的想法,这是相当随意和不尊重的
  • (不是反对者)这不是一个例子。什么都不做的方法总是线程安全的,static 与否。评论说修改cd,但cd 本身就是一个ThreadSafe ConcurrentDictionary,所以这就是为什么可以。
  • @weston 这就是为什么我在内部修改一个类型,静态 ConcurrentDictionary,而不是对于非实例方法,我可以为字典做到这一点,直到我保持它对线程唯一,但是这不适用于静态方法,因此两者的代码相似,修改静态类型
  • 实例可以在不同的线程上使用!这是一个错误的假设。通常组件的设计者会阻止其他线程的访问,但它不是开箱即用的线程安全的!
  • @Schwarzie2478 主要取决于内部包含的类型,但是如果在静态类型这样的类型中没有什么需要显式线程安全,那么每个线程都有一个单独的实例就像给每个线程自己的内存一样一起工作。我明白了您的情况,但是用户需要确保在用于多线程的类型中不存在此类线程安全问题

标签: c# .net multithreading


【解决方案1】:

静态方法实现线程安全吗?

不,如果它们修改了共享数据,那么它们就是非线程安全的。您的示例可能没问题,但这只是因为共享数据本身是线程安全的,它是不可变类型的ConcurrentDictionary (ints)。

实例方法当然是线程安全的,如果我们为每个线程分配一个单独的实例

不,如果一个实例由单个线程访问,那么这不会使其成为线程安全的。这只是避免了多线程问题。

总之static与多线程无关。

【讨论】:

  • 是的,实例类型避免了多线程同步场景并且仍然可以完成工作,但是静态很容易受到线程安全问题的影响,因为所有线程都访问相同的内存,它们当然需要特别小心。静态方法无论如何都需要静态类型才能工作,实例方法没有这样的绑定
  • 好吧,我不知道“仍在工作”部分。因为您无法通过实例和静态实现相同的效果。
  • 我的意思是在我的类型位于 Web api 后面的场景中,为 UI 获取 json,我保留所有业务层对象实例类型以调用 DAL,获取数据并发送作为响应,BL 在内部不维护任何对象,因此实例方法可以正常工作。为了维护数据,我必须缓存它们或创建静态对象。现在我正在那里构建内存缓存以提高性能,这将是静态的以保留数据,并且在进行调用时仍会在实例方法中进行修改。我希望这可以为我所说的一个案例提供一个简洁的视图
  • 对不起,它并没有让它更清晰。底线是static 与线程安全无关,它只是意味着有一个实例。
  • 当它们具有写访问权限时,并不是一个实例意味着线程损坏的可能性更高。虽然我理解了本质,但方法不是内存分配,因此实例/静态方法与线程安全无关,它是导致线程安全问题的类型。事实上,这就是我在问题中提到的
【解决方案2】:

线程安全与类和类的实例无关。两者都可以以不安全的方式用于线程。

但是通常像 winforms 控件这样的对象不能从其他线程访问它们的资源,因此它们会检查您是否从其他线程访问它们,并且您必须确保使用 Invoke 为该控件使用所需的线程...

【讨论】:

  • 同意,但这是由 Winforms 生成的 UI 的一个非常具体的场景,通常需要 BackgroundWorker 使用托管/启动 winform 类对象的同一线程安全访问
  • 不,不,是控件本身处理 UI 访问的东西,backgroundworker 只能使用线程中立的控件的方法......线程作为控件(这通常是非常错误的:-))
  • 我认为我的评论传达了一个不正确的信息,我的意思是 BackgroundWorker 可以帮助以线程安全的方式访问控制数据更新。这是一个 Workaorund,当然不在同一个线程上,它会完全破坏目的。无论如何,我与winforms无关,所以我可以放心,这种情况不会妨碍我:)
  • 但是你的答案很清楚吗,因为我感觉到你对我们的答案并不完全满意......
  • 我正在进一步测试,以便可以在同一个线程中提出疑问
【解决方案3】:

静态方法不是线程安全的因为它们是静态的。

它们是线程安全的,因为有人让它们成为线程安全的。通常在 .NET 框架中,静态方法是线程安全的因为有人这样编写它们

您可以轻松编写非线程安全的静态成员,这里没有什么神奇之处。

编写线程安全的实例成员必须遵循完全相同的规则来编写线程安全的静态成员。

【讨论】:

  • 让我理解您所说的,通过 deefaukt 执行 .net 中的静态方法被设计为线程安全的,尽管用户有足够的机会在其中执行可能使其不安全的事情多线程
  • 不,这是不正确的。静态成员绝不是暗示或与线程安全相关的任何事情。静态方法是线程安全的,完全由程序员决定,他们确保以线程安全的方式编写它。换句话说,你倒退了因果。 约定是为了确保静态方法是线程安全的。
【解决方案4】:
instance methods are certainly thread safe, if we assign a separate instance to each thread

是的,当一个线程构造一个对象时,只有这个线程有对该对象的引用,没有其他线程可以访问该对象,并且在调用实例方法时不需要线程同步。

线程安全并不意味着同步

线程安全意味着如果两个线程同时尝试访问数据,数据不会被破坏。线程安全还取决于您正在读取和写入适合单个单词的数据类型( int 在 32 位处理器上,long 在 64 位处理器上)是线程安全的。

同步是实现线程安全的一种方式,但对象的不变性也是如此。

回到您的问题,例如,如果您的线程通过静态字段公开对对象的引用并将其作为状态参数传递给另一个线程的方法,如果线程可以尝试同时写入访问(但不是读取-仅与读写访问不同)

因此,拥有一个可以被多个线程同时读写的对象(与方法无关)(静态或实例)应该是线程安全的。

【讨论】:

  • 您的第一句话不一定正确。如果实例方法访问可变静态成员,那么“线程安全”就会消失。
  • @JimMischel 这是真的,因为静态成员是堆内称为高频堆的特殊区域,与实例无关,但这里我们假设对象中没有定义静态成员
猜你喜欢
  • 1970-01-01
  • 2011-07-07
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2010-11-08
  • 2018-01-17
  • 2016-07-16
相关资源
最近更新 更多