使用任务,“索引超出范围”和IsCompleted过早返回true

时间:2014-05-10 18:13:52

标签: c# multithreading task

我试图在" Windows Store"中测试我对(并了解)任务的理解。 C#。我制作了一个多任务应用程序,其中包含多个进度条和模拟任务,这些任务在不同的时间段内计为100。它最终将成为多线程文件解析器的基础。

我有一个任务处理任务" parseFiles"通过检查计数器,循环遍历排队的任务列表,直到它们全部完成。

无论如何,当这个程序运行时,我随机获得索引的第一次机会异常超出范围变化的任务处理循环的" parseFiles"任务。循环计数器,i,以某种方式被设置为高于可能的值,即3,即使for循环显然只将i设置为最大值2。

发生的另一件奇怪的事情是"解析"解析器[]中的任务有时会为IsCompleted返回一个真值,即使它们还没有开始。

关于导致这些奇怪错误的原因的任何想法?我在这里做错了吗?

我很乐意接受任何有关排队和处理解析任务的更强大或更简单的方法的建议,因为它可能会修复这两个错误。

以下是代码:

namespace TaskTest
{
public sealed partial class MainPage : Page
{
    testFile[] fileList;
    public MainPage()
    {
        this.InitializeComponent();
        fileList = new testFile[10];
        for(int j=0; j<10; j++){
            fileList[j] = new testFile();
        }
    }

    private static void parseFile(testFile file, IProgress<int> progress)
    {
        progress.Report(0);
        file.parse(progress);
    }

    public Task parseFiles(IProgress<int>[] progresses) {
        int nParsers = 3;
        int nParsers = 3;
        if (nParsers > fileList.Length)
        {
            nParsers = fileList.Length;
        }
        Task[] parsers = new Task[nParsers];
        Boolean parseCompleted = false;

        Task.Run(() =>
            {
                //Do the parsing
                int fileCounter = -1;
                Boolean startedLast = false;
                Boolean[] finalTasksDone = new Boolean[nParsers];
                while (!parseCompleted)
                {

                    for(int i=0; i<=(nParsers - 1); i++){
                        if (!startedLast && (parsers[i] == null || parsers[i].IsCompleted))
                        {
                            fileCounter++;
                            Debug.WriteLine("Task " + i + " completed. Starting new Task " + i + " to parse file #" + fileCounter);
                            if (fileCounter == fileList.Length - 1) { startedLast = true; }
                            Action<testFile, Progress<int>> parseAction = parseFile;
                            parsers[i] = Task.Factory.StartNew(() => parseFile(fileList[fileCounter], progresses[i]));
                        }
                        else
                        {
                            if (startedLast && parsers[i].IsCompleted)
                            {
                                finalTasksDone[i] = true;
                            }
                        }
                    }
                    int finishedCounter = 0;
                    for (int i = 0; i <= (nParsers - 1); i++)
                    {
                        if (finalTasksDone[i])
                        {
                            finishedCounter++;
                        }
                    }
                    if (finishedCounter >= nParsers)
                    {
                        parseCompleted = true;
                    }
                }
            });
        return null;
    }


    public class testFile{

        public async void parse(IProgress<int> progress){
            Random random = new Random();
            int simDelay = 1;
            int simFileLength = random.Next(50, 100);
            Debug.WriteLine("Starting file with length: " + simFileLength);
            for(int j=0; j<simFileLength; j++){
                await Task.Delay(TimeSpan.FromSeconds(simDelay));
                int percentProgress = (int) (((double) j/ (double) simFileLength) * 100);
                progress.Report(percentProgress);
            }
        }
    }

    private void button_Click(object sender, RoutedEventArgs e)
    {
        IProgress<int>[] progresses = new Progress<int>[3];
        progresses[0] = new Progress<int>(ReportProgress1);
        progresses[1] = new Progress<int>(ReportProgress2);
        progresses[2] = new Progress<int>(ReportProgress3);
        parseFiles(progresses);
    }
    private void ReportProgress1(int value)
    {
        pBar1.Value = value;
    }
    private void ReportProgress2(int value)
    {
        pBar2.Value = value;
    }
    private void ReportProgress3(int value)
    {
        pBar3.Value = value;
    }

}


}

这是来自典型跑步的日志。 据此,大多数任务甚至在IsCompleted返回true之前都没有开始。

Task 0 completed. Starting new Task 0 to parse file #0
Task 1 completed. Starting new Task 1 to parse file #1
Task 2 completed. Starting new Task 2 to parse file #2
A first chance exception of type 'System.IndexOutOfRangeException' occurred in TaskTest.exe
Additional information: Index was outside the bounds of the array.

A first chance exception of type 'System.IndexOutOfRangeException' occurred in  TaskTest.exe
Additional information: Index was outside the bounds of the array.

Task 1 completed. Starting new Task 1 to parse file #3
Task 2 completed. Starting new Task 2 to parse file #4
A first chance exception of type 'System.IndexOutOfRangeException' occurred in TaskTest.exe
Additional information: Index was outside the bounds of the array.

Starting file with length: 73
Starting file with length: 83
Task 2 completed. Starting new Task 2 to parse file #5
A first chance exception of type 'System.IndexOutOfRangeException' occurred in TaskTest.exe
Additional information: Index was outside the bounds of the array.

Task 2 completed. Starting new Task 2 to parse file #6
A first chance exception of type 'System.IndexOutOfRangeException' occurred in TaskTest.exe
Additional information: Index was outside the bounds of the array.

Task 1 completed. Starting new Task 1 to parse file #7
Task 2 completed. Starting new Task 2 to parse file #8
Starting file with length: 78
Task 1 completed. Starting new Task 1 to parse file #9
A first chance exception of type 'System.IndexOutOfRangeException' occurred in TaskTest.exe
Additional information: Index was outside the bounds of the array.

A first chance exception of type 'System.IndexOutOfRangeException' occurred in TaskTest.exe
Additional information: Index was outside the bounds of the array.

The program '[7372] TaskTest.exe' has exited with code 1 (0x1).

1 个答案:

答案 0 :(得分:0)

我在这段代码中看到的主要问题是parseFiles()在很多情况下会过早退出。假设解析器0已经完成,解析器1和2仍有很长的路要走,当你启动所有文件时startedLast为真。现在,while (!parseCompleted)循环将执行,并且在for循环中,将发现解析器0已完成并将lastTasksCount递增为1.现在,while (!parseCompleted)循环再次执行,并在for中循环,再次发现解析器0已完成,并将lastTasksCount递增到2.这种情况再次发生,lastTasksCount递增到3,parseCompleted设置为true,并且方法退出,解析器1和解析器2仍在工作。我想也许如果你修复了这个bug,其余的症状就会消失 - 或者如果暴露出另一个bug就会改变。

关于您的具体症状,您并未说明您认为当任务尚未开始时IsComplete()返回true的结果是什么特定症状。在这方面有两件事可能会产生误导。一个是上面的parseFiles,当某些任务仍在进行时退出。另一个是以&#34;任务X开始的调试语句完成&#34;将在解析器X尚未存在时,在解析器X插槽第一次填充之前打印。生成的调试语句可能有点误导。

我并没有真正看到i越界的方法,但如果文件数少于解析器的数量,则会有空指针访问。例如,如果有一个文件和两个解析器,第一次通过for循环将为该文件创建解析器0,startsLast将设置为true,然后下一次通过{{1循环测试for将在解析器[1]仍为空时执行。这不会导致写入的常量出现问题,但是,因为你有10个大于3的文件,你拥有的解析器数量。

同样,我会解决我在第一段中提到的第一个错误,以及解析器数量超过文件数时可能出现的错误,看看是否能解决问题。