C# 我的多线程创建或覆盖额外/现有线程

C# 我的多线程创建或覆盖额外/现有线程,c#,.net,multithreading,C#,.net,Multithreading,我想将测试分散到多个线程上,因此首先我将要测试的总线程数除以要测试的线程数(包括余数)。然后,我为每个线程分配一系列数字。每次检测呈阳性时,计数器都会递增。我在0到123的范围内测试它,因为我知道结果应该是什么,但是每当我为任务分配超过1个线程时,我都会得到错误的结果。调试时,我注意到在线程启动后,当前行会跳回到指定新线程的位置。我不明白为什么。我的柜台上有一把锁,据我所知,这把锁工作正常。以下是相应的代码: for (int n = 0; n < remainder; n++) {

我想将测试分散到多个线程上,因此首先我将要测试的总线程数除以要测试的线程数(包括余数)。然后,我为每个线程分配一系列数字。每次检测呈阳性时,计数器都会递增。我在0到123的范围内测试它,因为我知道结果应该是什么,但是每当我为任务分配超过1个线程时,我都会得到错误的结果。调试时,我注意到在线程启动后,当前行会跳回到指定新线程的位置。我不明白为什么。我的柜台上有一把锁,据我所知,这把锁工作正常。以下是相应的代码:

for (int n = 0; n < remainder; n++)
{
      workers[n] = new Thread(() => CountNumbers(startvalue + n * (tasks + 1), startvalue + (n + 1) * (tasks + 1), modulus));
}
for (int m = remainder; m < nrthreads; m++)
{
      workers[m] = new Thread(() => CountNumbers(startvalue + remainder * (tasks + 1) + (m - remainder) * tasks, startvalue + remainder * (tasks + 1) + (m - remainder + 1) * tasks, modulus));
}
for (int k = 0; k < nrthreads; k++)
{
      workers[k].Start();
}
for (int k = 0; k < nrthreads; k++)
{
      workers[k].Join();
}
for(int n=0;n<余数;n++)
{
workers[n]=新线程(()=>countnumber(startvalue+n*(tasks+1)、startvalue+(n+1)*(tasks+1)、模数));
}
for(int m=余数;mcountnumber(起始值+余数*(任务+1)+(m-余数)*任务,起始值+余数*(任务+1)+(m-余数+1)*任务,模数));
}
对于(int k=0;k
当所有k的
workers[k].Start()
都完成时,会出现“问题”,然后出于某种原因,它会覆盖
workers
中的最后一个线程


我对C#比较陌生,所以我很难找到缺陷。这是给学校的,所以正确方向的提示可能比清晰的答案更合适。

这是C#并行编程中的一个常见错误。当您通过lambda表达式声明匿名函数时,它们会捕获它们引用的任何变量(而不是值)。在本例中,所有线程都为
n
m
计数器捕获相同的变量实例,并引导所有线程执行以查看其最后一个值

解决这个问题的一个简单方法是在循环范围内声明另一个变量,并将计数器复制到它。由于作用域仅限于循环,因此变量不会在线程之间共享

for (int nOuter = 0; nOuter < remainder; nOuter++)
{
    int n = nOuter;
    workers[n] = new Thread(() => CountNumbers(startvalue + n * (tasks + 1), startvalue + (n + 1) * (tasks + 1), modulus));
}
for (int mOuter = remainder; mOuter < nrthreads; mOuter++)
{
    int m = mOuter;
    workers[m] = new Thread(() => CountNumbers(startvalue + remainder * (tasks + 1) + (m - remainder) * tasks, startvalue + remainder * (tasks + 1) + (m - remainder + 1) * tasks, modulus));
}
for(int-nOuter=0;nOuter<余数;nOuter++)
{
int n=零;
workers[n]=新线程(()=>countnumber(startvalue+n*(tasks+1)、startvalue+(n+1)*(tasks+1)、模数));
}
对于(int-mOuter=余数;mOutercountnumber(起始值+余数*(任务+1)+(m-余数)*任务,起始值+余数*(任务+1)+(m-余数+1)*任务,模数));
}
编辑:如果切换到使用PLINQ或TPL结构,则可以简化代码。以下内容应等同于您的整个逻辑:

Parallel.For(0, nrthreads, k =>
{
    if (k < remainder)
        CountNumbers(startvalue + k * (tasks + 1), startvalue + (k + 1) * (tasks + 1), modulus);
    else
        CountNumbers(startvalue + remainder * (tasks + 1) + (k - remainder) * tasks, startvalue + remainder * (tasks + 1) + (k - remainder + 1) * tasks, modulus);
});
Parallel.For(0,nrthreads,k=>
{
if(k<余数)
计数数(起始值+k*(任务+1)、起始值+(k+1)*(任务+1)、模数);
其他的
计数数(起始值+余数*(任务+1)+(k-余数)*任务,起始值+余数*(任务+1)+(k-余数+1)*任务,模数);
});

由于不同步,您丢失了某个锁。我以前也有过类似的问题,只是我在迭代一个列表,并且我有一个有效的索引值(在使用list.Count时不可能超出范围),但它确实存在。因为我没有合适的锁。

问题可能出在CountNumbers方法中

你有没有考虑过使用Parallels.For或ForEach循环,而不是自己做所有“艰苦”的工作?我没有,主要是因为我对自己的数学部分有信心,而对更高级的循环则没有信心。我想它会清理一下代码。是的,这很有效。我碰巧经常遇到这样的问题,因为我习惯了Haskell,那里几乎从来没有出现过这样的问题。必须习惯变量的共享,这是一个不幸的陷阱。埃里克·利珀特(Eric Lippert)在他的博客中写道。Microsoft修复了C#5中
foreach
循环的行为,但没有修复
for
循环的行为。