Warning: file_get_contents(/data/phpspider/zhask/data//catemap/9/csharp-4.0/2.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
C# 4.0 任务并行库和循环_C# 4.0_Task Parallel Library - Fatal编程技术网

C# 4.0 任务并行库和循环

C# 4.0 任务并行库和循环,c#-4.0,task-parallel-library,C# 4.0,Task Parallel Library,我遇到了这样一种情况:我正在创建的任务似乎只有在调试代码时才起作用 正如您将在下面看到的,我不断得到一个索引超出范围的异常,这应该是不可能的,因为循环永远不会达到5 如果我在循环中添加一个断点,然后一直按F10直到程序结束,所有操作都会按预期进行 你知道我做错了什么吗 using System; using System.Collections.Generic; using System.Threading.Tasks; namespace ConsoleApplication1 { clas

我遇到了这样一种情况:我正在创建的任务似乎只有在调试代码时才起作用

正如您将在下面看到的,我不断得到一个索引超出范围的异常,这应该是不可能的,因为循环永远不会达到5

如果我在循环中添加一个断点,然后一直按F10直到程序结束,所有操作都会按预期进行

你知道我做错了什么吗

using System;
using System.Collections.Generic;
using System.Threading.Tasks;

namespace ConsoleApplication1
{
class Program
{        
    static void Main(string[] args)
    {
        int[] numbers = new int[5] { 1, 2, 3, 4, 5 };
        List<int> nums = new List<int>();

        var tasks = new Task[5];

        for (int i = 0; i < numbers.Length; i++)
        {
            tasks[i] = Task.Factory.StartNew(() =>
                {
                    nums.Add(numbers[i]);
                }, 
                TaskCreationOptions.None);
        }
        Task.WaitAll(tasks);

        for (int i = 0; i < nums.Count; i++)
        {
            Console.WriteLine(nums[i]);
        }
        Console.ReadLine();
    }
}
使用系统;
使用System.Collections.Generic;
使用System.Threading.Tasks;
命名空间控制台应用程序1
{
班级计划
{        
静态void Main(字符串[]参数)
{
int[]number=新的int[5]{1,2,3,4,5};
List nums=新列表();
var任务=新任务[5];
for(int i=0;i
{
增加(数字[i]);
}, 
任务创建选项。无);
}
Task.WaitAll(任务);
对于(int i=0;i
}

我希望看到1、2、3、4、5,但不是以任何特定顺序运行异步

奇怪的是,这确实有效,但除了额外的打字之外,我看不出有什么区别

using System;
using System.Collections.Generic;
using System.Threading.Tasks;

namespace ConsoleApplication1
{
class Program
{        
    static void Main(string[] args)
    {
        int[] numbers = new int[5] { 1, 2, 3, 4, 5 };
        List<int> nums = new List<int>();

        var tasks = new Task[5]
        {
            Task.Factory.StartNew(() => {nums.Add(numbers[0]);}, TaskCreationOptions.None),
            Task.Factory.StartNew(() => {nums.Add(numbers[1]);}, TaskCreationOptions.None),
            Task.Factory.StartNew(() => {nums.Add(numbers[2]);}, TaskCreationOptions.None),
            Task.Factory.StartNew(() => {nums.Add(numbers[3]);}, TaskCreationOptions.None),
            Task.Factory.StartNew(() => {nums.Add(numbers[4]);}, TaskCreationOptions.None)
        };

        Task.WaitAll(tasks);

        for (int i = 0; i < nums.Count; i++)
        {
            Console.WriteLine(nums[i]);
        }
        Console.ReadLine();
    }
}
使用系统;
使用System.Collections.Generic;
使用System.Threading.Tasks;
命名空间控制台应用程序1
{
班级计划
{        
静态void Main(字符串[]参数)
{
int[]number=新的int[5]{1,2,3,4,5};
List nums=新列表();
var任务=新任务[5]
{
Task.Factory.StartNew(()=>{nums.Add(数字[0]);},TaskCreationOptions.None),
Task.Factory.StartNew(()=>{nums.Add(number[1]);},TaskCreationOptions.None),
Task.Factory.StartNew(()=>{nums.Add(number[2]);},TaskCreationOptions.None),
Task.Factory.StartNew(()=>{nums.Add(number[3]);},TaskCreationOptions.None),
Task.Factory.StartNew(()=>{nums.Add(number[4]);},TaskCreationOptions.None)
};
Task.WaitAll(任务);
对于(int i=0;i
}

谢谢


Mike

由于任务发生在循环之外,而循环在任务执行时已经完成,此时i为5(循环退出条件)

在第二个示例中,您不使用i,而是对值进行硬编码,以便问题消失

在调试器中,时间是不同的,任务可以在循环完成之前执行:同样,问题消失了

我想你可以这样修理它:

var ii=i;
tasks[i] = Task.Factory.StartNew(() =>
                {
                    nums.Add(numbers[ii]);
                }, 
                TaskCreationOptions.None);

这里的问题是,任务访问i变量,在任务运行时,该变量将被更改,并且在上一次迭代中获得异常的原因是变量i将为5,虽然循环将在该增量后停止,但任务体仍引用它,因此此行将抛出

nums.Add(numbers[i]);
因为很明显5超出了范围值

要解决这个问题,需要将i作为状态参数传递给StartNew方法

 tasks[i] = Task.Factory.StartNew((obj) =>
            {
                int index = (int) obj;
                nums.Add(numbers[index]);
            }, 
            i);

更新:在C#5中,这不再是一个问题。使foreach的循环变量在逻辑上位于循环内部的方法

这是您在C#代码中可能犯的最常见错误之一的另一个示例:在匿名委托中捕获循环变量。埃里克·利珀特对到底出了什么问题有自己的看法

事实上,你的情况甚至更糟,因为理论上它可能会随机出现对错。假设您进行了以下更改:

for (int i = 0; i < numbers.Length; i++)
{
    tasks[i] = ...;
    tasks[i].Wait();
}

Tom的答案通过在循环中引入新变量
ii
解决了这个问题。创建每个任务时,它捕获这个变量,每个迭代都有一个固定的值。

请澄清,您是说第一段代码不起作用,但第二段代码起作用,对吗?我想这就是Resharper在哀求您“访问修改的闭包”时所说的:作为进一步说明,我发现使用tasks=newlist()和tasks.Add(mytask)更容易;然后执行Task.WaitAll(tasks.toArray());
i = 0
Create task 0
i = 1
Run task 0: This task sees i = 1
Create task 1
i = 2
Create task 2
i = 3
Create task 3
i = 4
Create task 4
i = 5
Run task 1: This task sees i = 5 and throws an exception