线程安全和任务工厂

本文关键字:工厂 任务 安全 线程 | 更新日期: 2023-09-27 18:34:38

我在并行编程
方面很陌生我想做一些任务
工作每个任务都是使用 param 启动的,以使用 id
做一些简单的工作但似乎参数都混在一起了..

确定我缺少线程安全
中的一些关键元素你能帮我了解我做错了什么吗?

不需要任务的任何返回值,我只需要它们来完成工作。

static void Main(string[] args)
{
    int NumberOfTasks = 10;
    Task[] tasks = new Task[NumberOfTasks];
    for (int i = 0; i < NumberOfTasks; i++)
    {
       tasks[i] = Task.Factory.StartNew(() => DoSafeWork(i));
    }
    Task.WaitAll(tasks);
    Console.WriteLine("Done !");
    Console.ReadKey();
}
private static void DoSafeWork(int i)
{
    Console.WriteLine("working on Task {0} ", i.ToString());
}

电流输出(可能(:

working on Task 3    
working on Task 6    
working on Task 10    
working on Task 10    
working on Task 10    
working on Task 10    
working on Task 10    
working on Task 10    
working on Task 10    
working on Task 10    
Done !

线程安全和任务工厂

问题在于循环变量是在循环之外定义的。请注意,问题的根源实际上是闭包,与线程无关。

只需像这样创建循环变量的本地副本,以便闭包捕获副本:

for (int i = 0; i < NumberOfTasks; i++)
{
    var localCopy = i;
    tasks[i] = Task.Factory.StartNew(() => DoSafeWork(localCopy));
}

有关详细说明,请参阅 https://stackoverflow.com/a/512265/219187。

正如 theDmi 所说,问题是您正在关闭索引,因此所有任务都引用相同的索引,而 for 循环会更改它。

虽然您可以通过将当前索引值复制到局部变量来解决此问题,但更优雅的解决方案是使用 LINQ:

var tasks = Enumerable.Range(0, NumberOfTasks).Select(i => Task.Factory.StartNew(() => DoSafeWork(i)));
Task.WaitAll(tasks);

此外,如果您不局限于旧版本的 .Net,则应改用 Task.RunTask.WhenAllawait

var tasks = Enumerable.Range(0, NumberOfTasks).Select(i => Task.Run(() => DoSafeWork(i)));
await Task.WhenAll(tasks);