局部变量是否线程安全?

9

我有一个类,就像下面这样:

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传递,它是一个不可变类型。因此,它显然不会出现问题。 - Jeppe Stig Nielsen
1
抱歉,我刚才讲的是Display方法中的iMain方法中的i被lambda捕获了,这显然是有问题的。 i被转换成一个字段,它只会在一个实例中出现。你可能想要这样做:for(int i = 0; i <10; i ++){ int copyOfI = i; var thread = new Thread(()=> action()){Name =“Thread”+ copyOfI}; threads.Add(thread); }。这也在Henk的答案中提到过。这样一来,copyOfI将成为生成类上的一个实例字段,并且将有十个该类的实例。 - Jeppe Stig Nielsen
我之前的评论可能是错误的。我认为可以这样说:new Thread(() => action()) /* 匿名 lambda 在此处停止 */ {Name = "Thread " + i /* 不会捕获 */ }; - Jeppe Stig Nielsen
3个回答

18

每次方法调用都会有一组独立的局部变量。然而,这些变量可能引用其他方法也使用的对象。例如:

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

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


5
只要你的数据是本地的,那么它就是线程安全的。如果它引用了该方法之外的数据,则不是线程安全的。
在你的例子中,i不会受到多个线程的影响(因为你创建了变量,设置了本地值并且只是增加了该值)。
如果i引用了方法之外的一些东西(例如文件),那么它就不是线程安全的,因为你可能会在不同的线程上引用同一个文件。

1

只要您不将局部变量传递给其他线程,它们在方法内部是线程安全的。如果您这样做了,则取决于情况。

例如,在您的代码中,创建的所有线程都共享访问outputWindowthreads。如果这些对象不是线程安全的,并且您从多个线程调用它们,则可能会遇到问题。例如,如果每个线程在完成时都尝试从threads中删除自身,则会出现问题,因为List<>对于读/写不是线程安全的。

您的所有线程都使用同一个OutputWindow实例。如果对Display的调用改变了对象的状态,则需要确保该变化是线程安全的,否则就会出现竞态条件。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接