【问题标题】:Threading: Why does this code show 5 in output in thread [duplicate]线程:为什么此代码在线程的输出中显示 5 [重复]
【发布时间】:2017-03-14 15:54:32
【问题描述】:

我有一个简单的代码,它有一个锁并创建了 5 个线程。

static readonly object _object = new object();

static void A(int currentValue)
{
    lock (_object)
    {
        Console.WriteLine(currentValue + " Start");                
        Console.WriteLine(currentValue + " End");
    }
}

static void Main(string[] args)
{
    for (int i = 0; i < 5; i++)
    {
        Thread t = new Thread(() => A(i));
        t.Name = "Thread " + i;
        Console.WriteLine(t.Name + " Created");
        t.Start();                
    }
    Console.ReadKey();
}

当我运行此代码时,输​​出中会显示以下内容。

Thread 0 Created
Thread 1 Created
1 Start
1 End
Thread 2 Created
2 Start
2 End
Thread 3 Created
3 Start
3 End
Thread 4 Created
4 Start
4 End
5 Start
5 End

当我只创建 5 个线程 (0 - 4) 时。为什么我会看到 5 Start 和 5 End 的条目?

【问题讨论】:

  • 尝试前增量而不是后增量,例如++i
  • 我不确定根本原因,但我想这些是相关的:我注意到您没有“0 Start”和“0 End”行。看起来 i 在每个线程运行之前都在增加,但对于“线程创建”行仍然是相同的 i。我最好的猜测是,当线程运行时,它采用 i 的最新值,而不是声明线程变量时 i 的值。您是否始终获得相同的结果? (另请参阅 Peter Duniho 的回答 - 非常相关)
  • 问题是在线程启动的时候变量i已经改变了。你很幸运,任何线程甚至在整个循环完成之前就开始了。通常,当您编写这样的错误时,行为是 所有 线程打印5。有关修复它的详细信息,请参阅标记重复(Lasse 的答案)。
  • 更重要的是,无论代码中是否存在lock 语句,所描述的行为都将保持不变,所以问题是 - 为什么 存在lock 语句?
  • @MongZhu 我怀疑在现实世界中A 会在最终锁定之前做一些可并行化的工作,以便写入Console,或者将输出添加到集合中,或者做一些其他不-线程安全。

标签: c# multithreading


【解决方案1】:

问题是当一个线程被启动并且A的调用发生时,编译器会短暂跳转到这一行去获取参数:

Thread t = new Thread(() => A(i));

如果此时循环已经完成,变量i 的值将是5。所以它会采用这个值。我猜这种现象称为闭包。这是Jon Skeet的文章The Beauty of Closures

如果您将i 值保存到像这样新声明的时间变量中,您就可以摆脱它,并且 5 将不再出现

for (int i = 0; i < 5; i++)
{
    int temp = i;
    Thread t = new Thread(() => A(temp));
    t.Name = "Thread " + i;
    t.Start();
}

这是answer to a very similar problem

lock 在这种情况下实际上是不必要的,因为您只能访问方法 A 中的局部变量。每个线程都有自己的个人变量。锁实际上禁止线程并行运行。除非你想要这种方式,但你不需要线程。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2013-09-24
    • 1970-01-01
    • 1970-01-01
    • 2012-07-21
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多