线程本地存储和本地方法变量

时间:2010-05-06 08:39:30

标签: c# multithreading

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

如果是这种情况,为什么以下代码不是线程安全的? (声明此帖子 在此帖子中是线程安全的:Locking in C#

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

由于count是一个int(堆栈变量),当然这个值会被隔离到一个单独的线程,在它自己的堆栈上,因此是线程安全的吗? < / p>

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

此外,本地声明的变量如何:

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

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

3 个答案:

答案 0 :(得分:3)

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

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

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

答案 1 :(得分:2)

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

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

但是,仍然有麻烦的余地。如果在线程中存储对TLS中的Foo的引用,则它最初对其他线程不可见。如果你以某种方式将该引用复制到另一个线程中,那么无论第一个引用是否存在于TLS中,你都会处于线程不安全的状态。

答案 2 :(得分: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);
}