【问题标题】:Threading in C#: How to keep loop-index thread-safe?C#中的线程:如何保持循环索引线程安全?
【发布时间】:2016-07-11 13:59:44
【问题描述】:

在下面的例子中,似乎for循环的索引i被每个线程独立修改,导致DoWork_Threaded()i的奇怪值(倍数,甚至大于System.Environment.ProcessorCount-1的值)

如何在 C# 中正确完成多线程循环?

// Prepare all threads
Thread[] threads = new Thread[System.Environment.ProcessorCount];

// Start all threads
for (int i = 0; i < System.Environment.ProcessorCount; i++)
{
   threads[i] = new Thread(() => DoWork_Threaded(i));
   threads[i].Start();
}

// Wait for completion of all threads
for (int i = 0; i < System.Environment.ProcessorCount; i++)
{
   threads[i].Join();
}

【问题讨论】:

  • 感谢您的回答,但这对索引 i 有何帮助? PS:它也不起作用。
  • 尝试复制循环内的值int a = i; 然后DoWork_Threaded(a);
  • 要了解为什么您的代码表现得像这样,您可以阅读blogs.msdn.microsoft.com/ericlippert/2009/11/12/…。它也与线程完全无关。任何对循环变量的闭包都可能导致您描述的行为。
  • @Dirk:虽然问题的原因与线程无关,但从多个线程访问闭包会使问题变得更糟,因为变量的观察值变得不确定。
  • 你为什么不使用ThreadPool?看来你想实现类似的东西。对于索引部分,您可能需要阅读 this 以了解它为什么不起作用。

标签: c# multithreading thread-safety


【解决方案1】:

首先,与其滚动你自己的线程调度服务,为什么不使用任务并行库呢?它已经具有确定将线程调度到的处理器数量的逻辑。

要回答您的实际问题,您关闭了一个循环变量。请参阅我的文章,了解为什么这是错误的:

https://ericlippert.com/2009/11/12/closing-over-the-loop-variable-considered-harmful-part-one/

简而言之:i 是一个变量和变量变化。当您的 lambda 执行时,它会以 i当前 值执行,而不是它在创建委托时使用的值。

【讨论】:

  • 感谢您向我指出非常好的 TPL。然而,在我的例子中,我们有许多(百万,想想图像像素)元素,这些元素是手动处理的,分布在所有内核上。尽管与仅使用 Parallel.For 相比,这样做似乎容易出错,但它也快得多。我不知道 Parallel.For 如何分配工作,但我认为这是由于创建更多线程的开销......甚至可能是每个元素?
  • 有没有办法告诉 TPL 在这种情况下(Parallel.For 循环中的数百万个元素)(在 4 核的示例情况下)在第一季度使用核心 1,在第一季度使用核心 2第二季度等等?
  • @ares_games:这是一个问答网站;这听起来是个不错的问题,可以在这里发布,甚至可以发布到游戏网站上。
【解决方案2】:

在匿名方法和线程中使用局部变量可能会很棘手。您需要复制匿名方法正在使用的值,因为它正在更改:

for (int i = 0; i < System.Environment.ProcessorCount; i++)
{
     int a = i;
     threads[i] = new Thread(() => DoWork_Threaded(a));
     threads[i].Start();
}

这使我的机器上的输出变得合理。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2013-03-22
    • 2014-07-06
    • 2018-04-01
    • 1970-01-01
    • 1970-01-01
    • 2016-11-28
    相关资源
    最近更新 更多