Dan*_*han 6 .net c# multithreading asynchronous threadpool
请检查下面的代码示例:
public class Sample
{
public int counter { get; set; }
public string ID;
public void RunCount()
{
for (int i = 0; i < counter; i++)
{
Thread.Sleep(1000);
Console.WriteLine(this.ID + " : " + i.ToString());
}
}
}
class Test
{
static void Main()
{
Sample[] arrSample = new Sample[4];
for (int i = 0; i < arrSample.Length; i++)
{
arrSample[i] = new Sample();
arrSample[i].ID = "Sample-" + i.ToString();
arrSample[i].counter = 10;
}
foreach (Sample s in arrSample)
{
ThreadPool.QueueUserWorkItem(callback => s.RunCount());
}
Console.ReadKey();
}
}
Run Code Online (Sandbox Code Playgroud)
此示例的预期输出应类似于:
Sample-0 : 0
Sample-1 : 0
Sample-2 : 0
Sample-3 : 0
Sample-0 : 1
Sample-1 : 1
Sample-2 : 1
Sample-3 : 1
.
.
.
Run Code Online (Sandbox Code Playgroud)
但是,当您运行此代码时,它将显示以下内容:
Sample-3 : 0
Sample-3 : 0
Sample-3 : 0
Sample-3 : 1
Sample-3 : 1
Sample-3 : 0
Sample-3 : 2
Sample-3 : 2
Sample-3 : 1
Sample-3 : 1
.
.
.
Run Code Online (Sandbox Code Playgroud)
我可以理解,线程执行的顺序可能不同,因此计数不会以循环方式增加.但是,我无法理解,为什么所有的IDs都被显示为Sample-3,而执行显然是相互独立的.
不同的对象是否与不同的线程一起使用?
Ani*_*Ani 10
这是旧的修改后的闭包问题.您可能希望查看:Threadpools -类似问题的可能线程执行顺序问题,以及Eric Lippert的博客文章Closing over loop variable被认为对于理解该问题有害.
从本质上讲,你所获得的lambda表达式是捕获变量 s而不是声明lambda的变量值.因此,随后到变量的值的更改是给委托可见.的实例Sample的在其RunCount方法将运行将取决于由变量称为实例s 在委托实际执行点(其值).
此外,由于委托(编译器实际上重用了相同的委托实例)正在异步执行,因此无法保证每次执行时这些值是什么.您目前看到的是,在任何委托调用之前,foreach循环在主线程上完成(预期 - 在线程池上调度任务需要时间).因此,所有工作项最终都会找到循环变量的"最终"值.但这无法保证; 尝试在循环中插入合理的持续时间,您将看到不同的输出.Thread.Sleep
通常的解决方法是:
捕获'copy'变量而不是lambda中的循环变量.
foreach (Sample s in arrSample)
{
Sample sCopy = s;
ThreadPool.QueueUserWorkItem(callback => sCopy.RunCount());
}
Run Code Online (Sandbox Code Playgroud)现在每个工作项"拥有"循环变量的特定值.
在这种情况下的另一个选择是通过不捕获任何东西完全避开问题:
ThreadPool.QueueUserWorkItem(obj => ((Sample)obj).RunCount(), s);
Run Code Online (Sandbox Code Playgroud)