【问题标题】:Thread safety and local variables线程安全和局部变量
【发布时间】:2009-03-24 22:05:13
【问题描述】:

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

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

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

另外,我认为这个变量是共享状态是否正确?我在思维部门缺乏的是,这个变量将与不同的客户对象(例如 John、Paul 等)一起工作,所以是线程安全的,但这是有缺陷的思维,并且在并发编程方面有点缺乏经验。这听起来很幼稚,但是我在并发编码方面没有很多经验,就像我一般做的同步编码。

编辑:另外,函数调用 getFromDb() 不是问题的一部分,我不希望任何人猜测它的线程安全性,因为它只是一个调用,表明值是从获取数据的函数分配的从分贝。 :)

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

【问题讨论】:

    标签: c#


    【解决方案1】:

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

    除了如果:

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

    在以上两种场景中,有一些情况是可以暴露在栈外的。但我怀疑你是否也在这样做。

    请注意,字段(类中的变量)不是线程安全的,因为它们很容易暴露给其他线程。这对于static 字段更为明显,因为所有线程自动共享相同的字段(线程静态字段除外)。

    【讨论】:

    • +1 表示完整性(讨论匿名方法和迭代器块。静态类和线程安全是个人最容易掌握的概念。:)
    • 恕我直言,重要的是要澄清即使在列出的异常中,局部变量仍然对于每次调用该方法都是唯一的。也就是说,虽然它们可以在方法的上下文之外被访问,甚至可能以非线程安全的方式访问,但相对于上下文中的 same 局部变量,它们仍然是线程安全的。对同一方法的不同调用。
    【解决方案2】:

    您的语句有两个独立的部分 - 一个函数调用和一个赋值。

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

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

    【讨论】:

      【解决方案3】:

      只要变量是方法的局部变量,它就是线程安全的。如果它是一个静态变量,那么它不会是默认的。

      class Example
      {
        static int var1; //not thread-safe
      
        public void Method1()
         { int var2; //thread-safe
         }
      }
      

      【讨论】:

        【解决方案4】:

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

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

        【讨论】:

          【解决方案5】:

          i 将是“线程安全的”,因为每个线程都会按照您的建议在堆栈上拥有自己的 i 副本。真正的问题是 getFromDb() 线程的内容是否安全?

          【讨论】:

            【解决方案6】:

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

            如果您的 getFromDb() 正在读取,例如,oracle 序列或 sql server 自动增量字段,则数据库负责同步(在大多数情况下,不包括复制/分布式数据库),因此您可以安全地返回结果到任何调用线程。也就是说,数据库保证每次 getFromDB() 调用都会得到不同的值。

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

            【讨论】:

              【解决方案7】:

              i 在语法上是线程安全的。但是当你给实例变量赋值时,实例方法的返回值给i,那么共享数据就被多个线程操作了。

              【讨论】:

                猜你喜欢
                • 2015-07-07
                • 2017-03-07
                • 1970-01-01
                • 2021-12-15
                • 1970-01-01
                • 1970-01-01
                • 1970-01-01
                • 1970-01-01
                相关资源
                最近更新 更多