【问题标题】:Are local variables threadsafe?局部变量是线程安全的吗?
【发布时间】:2014-01-17 10:25:46
【问题描述】:

我有一个像下面这样的课程:

class Program
    {
        static void Main(string[] args)
        {
            var outputWindow = new OutputWindow();

            var threads = new List<Thread>();

            Action action = () => outputWindow.Display(20);

            for (int i = 0; i < 10; i++)
            {
                var thread = new Thread(() => action()) {Name = "Thread " + i};
                threads.Add(thread);
            }

            foreach (var thread in threads)
            {
                thread.Start();
            }
        }
    }

    public class OutputWindow
    {
        public void Display(int x)
        {
            for (int i = 0; i < x; i++)
            {
                Console.WriteLine(Thread.CurrentThread.Name + " Outputcounter: " + i);
            }
        }
    }

问题是——这个线程安全吗?这会导致显示方法内的局部变量 i 出现任何竞争条件吗?所有线程是否会按预期增加变量“i”的值(即它会增加值并且不会侵占其他线程的 i 值)

如果这是线程安全的,那么假设在方法中声明的任何局部变量始终是线程安全的并且共享变量是需要同步的变量是否安全?

谢谢, -迈克

【问题讨论】:

  • 您有一个局部变量(不是参数)action(它确实在 lambda 中捕获了另一个局部变量)。然后启动多个线程,为每个线程提供一个新的 lambda,该 lambda 捕获相同的 action 局部变量。 action 变量实际上已转换为字段。当然,它毕竟不是那么“本地化”。所以那些局部变量不是线程安全的。当您询问代码中的变量 i 时,它确实是本地变量。它是在方法内部声明的,它不被任何 lambda 捕获,它不通过 refout 传递,它是一个不可变类型。所以显然没有问题。
  • 对不起,我说的是Display方法中的iMain 方法中的 i 被捕获在 lambda 中。这显然是有问题的。 i 变成了一个只会出现在 one 实例中的字段。你可能想要这个:for (int i = 0; i &lt; 10; i++) { int copyOfI = i; var thread = new Thread(() =&gt; action()) {Name = "Thread " + copyOfI}; threads.Add(thread); }。 Henk 的回答中也提到了这一点。这样,copyOfI 将变成生成类的实例字段,并且该类将有 10 个实例。
  • 我认为我之前的评论毕竟是不正确的。可以说new Thread(() =&gt; action()) /* ANONYMOUS LAMBDA HAS STOPPED HERE */ {Name = "Thread " + i /* Not capturing */ };

标签: c# multithreading local-variables


【解决方案1】:

每个方法调用都有一组单独的局部变量。但是,这些变量可以引用其他方法也使用的对象。例如:

public void AppendSomething(StringBuilder builder)
{
    builder.Append("Something");
}

这里builder 仍然是一个局部变量(参数是局部变量)并且AppendSomething 的每次调用都会有一个独立的变量 - 但您可以使用相同的方法从多个线程调用该方法StringBuilder 引用,因此该方法不是线程安全的。 (因为StringBuilder 不是线程安全的。)

【讨论】:

    【解决方案2】:

    只要您的数据是本地的,是的,它是线程安全的。如果它引用了该方法之外的数据,则不是。

    在您的示例中,i 不受多个线程的影响(因为您创建了变量,设置了本地值并仅增加值)。

    如果我引用了方法之外的东西(例如文件),那么它就不是线程安全的,因为你可能在不同的线程上引用同一个文件。

    【讨论】:

      【解决方案3】:

      只要您不将局部变量传递给其他线程,局部变量在方法中就是线程安全的。如果你这样做,那就看情况了。

      例如,在您的代码中,您创建的所有线程都具有对outputWindowthreads 的共享访问权限。如果对象不是线程安全的并且您从多个线程调用它们,那么您可能会遇到问题。例如,如果每个线程在完成时都尝试从threads 中删除自己,那么您就会遇到问题,因为List&lt;&gt; 对于读/写来说不是线程安全的。

      您的所有线程都使用相同的OutputWindow 实例。如果对 Display 的调用改变了对象的状态,则需要确保该突变是线程安全的,否则您将遇到竞争条件。

      【讨论】:

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