线程安全和局部变量

时间:2009-03-24 22:05:13

标签: c#

如果我有这样的局部变量:

Increment()
{
    int i = getFromDb(); // get count for a customer from db 
};

这是一个增加的实例类(每次客户 - 一个实例对象 - 进行购买),这个变量线程是否安全?我听说局部变量是线程安全的,因为每个线程都有自己的堆栈等等。

另外,我是否正确地认为这个变量是共享状态?我在思维部门缺乏的是这个变量将与不同的客户对象(例如John,Paul等)一起使用,因此线程安全,但这是有缺陷的思维和并发编程的一点经验。这听起来很天真,但是我在并发编码方面没有很多经验,就像我一般的同步编码一样。

编辑:此外,函数调用getFromDb()不是问题的一部分,我不希望任何人猜测它的线程安全性,因为它只是一个调用来指示值是从一个获取数据的函数赋值的来自db。 :)

编辑2:此外,getFromDb的线程安全性得到保证,因为它只执行读取操作。

7 个答案:

答案 0 :(得分:33)

i被声明为本地(方法)变量,因此只有通常存在于Increment()的堆栈框架中 - 所以是的,i是线程安全的......(虽然我无法评论getFromDb)。

除了 if:

  • Increment是一个迭代器块(即使用yield returnyield break
  • i用于匿名方法(delegate { i = i + 1;})或lambda(foo => {i=i+foo;}

在上述两种情况下,有些情况下它可以暴露在堆栈外部。但我怀疑你是否也在做。

请注意, fields (类中的变量)是线程安全的,因为它们通常会暴露给其他线程。对于static字段,这一点更加明显,因为所有线程都自动共享相同的字段(线程静态字段除外)。

答案 1 :(得分:5)

您的陈述有两个独立的部分 - 函数调用和赋值。

赋值是线程安全的,因为该变量是本地的。此方法的每次不同调用都将获得自己的局部变量版本,每个版本都存储在内存中不同位置的不同堆栈帧中。

对getFromDb()的调用可能是也可能不是线程安全的 - 取决于它的实现。

答案 2 :(得分:2)

只要变量是方法的本地变量,它就是线程安全的。如果它是静态变量,则默认情况下不会。

class Example
{
  static int var1; //not thread-safe

  public void Method1()
   { int var2; //thread-safe
   }
}

答案 3 :(得分:1)

虽然你的int i是线程安全的,但你的整个案例可能不是线程安全的。正如你所说,你的int i是线程安全的,因为每个线程都有自己的堆栈跟踪,因此每个线程都有自己的i。但是,您的线程都共享同一个数据库,因此您的数据库访问不是线程安全的。您需要正确同步数据库访问,以确保每个线程只在正确的时刻看到数据库。

与通常的并发和多线程一样,如果只读取信息,则无需在数据库上进行同步。一旦两个线程尝试从您的数据库读取/写入同一组信息,您就需要进行同步。

答案 4 :(得分:1)

我将是“线程安全的”,因为每个线程将根据您的建议在堆栈上拥有它自己的i副本。真正的问题是getFromDb()线程安全的内容吗?

答案 5 :(得分:1)

我是一个局部变量,所以它不是共享状态。

如果你的getFromDb()正在读取oracle序列或sql server autoincrement字段,那么db正在处理同步(在大多数情况下,不包括复制/分布式DB),因此你可以安全地返回结果到任何调用线程。也就是说,DB保证每个getFromDB()调用都会获得不同的值。

线程安全通常是一点点工作 - 更改变量的类型很少会让您获得线程安全,因为它取决于线程如何访问数据。您可以通过重新编写算法来节省一些麻烦,以便它使用所有使用者同步的队列,而不是尝试编排一系列锁定/监视器。或者更好的是,如果可能的话,使算法无锁。

答案 6 :(得分:1)

我在语法上是线程安全的。但是,当您分配实例变量的值,实例方法返回值为i时,则共享数据由多个线程操纵。