【问题标题】:Thread Local Storage and local method variables线程本地存储和本地方法变量
【发布时间】:2010-05-06 17:33:49
【问题描述】:

在 C# 中,每个线程都有自己的堆栈空间。

如果是这样,为什么下面的代码不是线程安全的? (在这篇文章中声明此代码线程安全的:Locking in C#

class Foo 
{ 
    private int count = 0; 
    public void TrySomething()     
    { 
        count++; 
    } 
} 

由于 count 是一个 int(堆栈变量),所以这个值肯定会被隔离到单独的线程,在它自己的堆栈上,因此是线程安全的?

我可能在这里遗漏了一些东西,但如果不是线程的基于堆栈的变量,我不明白线程本地存储中的实际内容是什么?

另外,本地声明的变量呢:

class Foo 
{ 
    public void TrySomething(object myObj)     
    { 
       var localVariable = new object();
       localVariable = myObj;
    } 
} 

这里对局部变量有什么影响?它仍然是基于堆的吗?它是线程安全的吗?

【问题讨论】:

  • 而这类问题正是为什么当人们声称“结构存储在堆栈上”而没有对其进行限定时,我会跳上去……
  • 抱歉,您能解释一下吗?从下面所说的,类中包含的任何变量实际上都存储在堆上,而基于堆栈的结构中的仅基于堆栈的成员变量实际上是在堆栈上。这是真的吗?

标签: c# multithreading


【解决方案1】:

Count 是Foo 类的成员变量。由于Foo 是一种引用类型,它存储在堆而不是堆栈中。

如果您只创建了 1 个 Foo 对象并允许两个单独的线程调用 TrySomething() 方法,那么它们都将在同一个对象(存储在堆上)上调用该方法,并且都将尝试递增相同的成员,因为它是同一对象的一部分。 (见达林的sample code

结构体和类的区别不在于它们是存储在堆上还是栈上。这是一个普遍的误解。将类视为堆上存储的指针。将结构视为本地存储的直接值。在一个非常简单的层面上,这确实意味着结构通常存储在堆栈中,正如本例所示,这肯定不是在所有情况下都是正确的。

【讨论】:

  • 然而,这表明类中包含的任何变量实际上都存储在堆上,而基于堆栈的结构中的仅基于堆栈的成员变量实际上是在堆栈上。这是真的吗?
  • @Miguel:不要将它们视为“基于堆栈”的变量。这就是混乱开始的地方。它是值类型(结构)和引用类型(类)。没有要求将它们存储在特定的任何地方。正如您的代码所示,结构可以在堆上。区别实际上在于复制语义与引用语义,它与存储位置无关。忘记堆栈和堆。我建议你阅读this by Eric Lippert
【解决方案2】:

此代码不是线程安全的,因为两个线程可以在Foo 的同一个实例上执行此方法,这意味着count 变量将是相同的:

var foo = new Foo();
new Thread(foo.TrySomething).Start();
new Thread(foo.TrySomething).Start();

TrySomething 方法将使用相同的count 变量在两个不同的线程上同时执行,因此对这个count 变量的每次访问都必须同步:

public void TrySomething()     
{ 
    Interlocked.Increment(ref count);
} 

【讨论】:

  • +1 好答案。我喜欢代码示例和建议的修复。
【解决方案3】:

但是计数不是堆栈变量;它是一个类的成员变量。如果您将对 Foo 的实例的引用传递给另一个线程,则您处于非线程安全的情况。

线程本地存储 (TLS) 是一个与成员变量完全不同的概念。 TLS 将值与 thread 相关联,而不是与特定类相关联。将其视为全局变量(或类的静态成员变量),只是它们的作用域仅限于在单个线程中可见;其他线程甚至看不到。它们不存储在线程的堆栈中,而是存储在线程专用的特殊区域中。

不过,仍有麻烦的余地。如果您在线程中的 TLS 中存储对 Foo 的引用,它最初对其他线程是不可见的。如果您随后以某种方式将该引用复制到另一个线程中,那么您将再次处于线程不安全的情况,而不管第一个引用存在于 TLS 中的事实。

【讨论】:

    猜你喜欢
    • 2014-09-07
    • 1970-01-01
    • 1970-01-01
    • 2011-01-04
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多