使用列表作为线程启动例程的参数时出现超出范围的索引错误

本文关键字:范围 错误 索引 参数 线程 启动 例程 列表 | 更新日期: 2023-09-27 18:30:01

我正在编写一个 C# 程序,该程序需要为函数提供线程参数,以便该函数在单独的线程上正常运行。具体来说,其中一个参数是它应该访问的文件的字符串名称。问题是我正在将文件名存储在列表中,并且我正在访问列表中的值。但是,当我这样做时,在创建一两个线程后,我会收到一个超出范围的索引错误。我认为这是字符串列表是我的问题,但我知道索引并没有超出范围。

不确定我是否在传入参数的方式上做错了什么,或者还有什么可能出错的地方。

下面是我的 C# 代码示例(不包括调用函数的代码(:

for (int i = 0; i < 5; i++)
            {
                surfaceGraphDataNames.Add(String.Format(surfacePlotDataLocation+"ThreadData{0}.txt", i));
                try
                {
                    generateInputFile(masterDataLocation);
                }
                catch
                {
                    MessageBox.Show("Not enough data remaining to create an input file");
                    masterDataLocation = masterDataSet.Count - ((graphData.NumRootsUsed + 1) * (graphData.Polynomial + 1) - 1);
                    this.dataSetLabel.Text = String.Format("Current Data Set: {0}", masterDataLocation + 1);
                    return;
                }
                try
                {
                    //creates the data in a specific text file I hope
                    createSurfaceGraph(surfaceGraphDataNames[i]);
                    //start threads 
                    threadsRunning.Add(new Thread(() => runGnuplotClicks(surfaceGraphDataNames[i], masterDataLocation)));
                    threadsRunning[i].Start();
                }
                catch
                {
                    this.graphPictureBox1.Image = null;//makes image go away if data fails
                    MessageBox.Show("Gridgen failed to generate good data");
                }
                masterDataLocation++;
            }

使用列表作为线程启动例程的参数时出现超出范围的索引错误

看起来你必须做这样的事情:

threadsRunning.Add(new Thread(() => {
           var k = i;
           runGnuplotClicks(surfaceGraphDataNames[k], masterDataLocation)
          }
        ));

原因是当你使用变量 i 时,它是不安全的,因为当你的i++surfaceGraphDataNames尚未添加新项目时,异常会抛出,因为你的Thread几乎同时运行。

以下是导致异常的上下文:

for(int i = 0; i < 5; i++){
   //Suppose i is increased to 3 at here
   //Here is where your Thread running code which accesses to the surfaceGraphDataNames[i]   
   //That means it's out of range at this time because
   //the surfaceGraphDataNames has not been added with new item by the code below 
   surfaceGraphDataNames.Add(String.Format(surfacePlotDataLocation+"ThreadData{0}.txt", i));
   //....
}

更新

看起来上面的代码甚至无法工作,因为在调用实际ThreadStart之前增加了i。我认为您可以这样做以使其更安全:

var j = i;
threadsRunning.Add(new Thread(() => {
     var k = j;
     runGnuplotClicks(surfaceGraphDataNames[k], masterDataLocation)
    }
));

同步尝试:

Queue<int> q = new Queue<int>();
for(int i = 0; i < 5; i++){
  //.....
  q.Enqueue(i);
  threadsRunning.Add(new Thread(() => {       
     runGnuplotClicks(surfaceGraphDataNames[q.Dequeue()], masterDataLocation)
    }
  ));
  threadsRunning[i].Start();
}

我遇到了这样的问题,然后我使用线程。我确信索引没有超出范围,如果我尝试按断点停止然后继续,这种情况就不会发生。尝试使用任务而不是线程。它有效

最明显的问题是你正在关闭循环变量。构造 lambda 表达式时,任何变量引用都是对变量本身而不是其值的引用。请考虑从您的示例中获取的以下代码。

for (int i = 0; i < 5; i++)
{
  // Code omitted for brevity.
  new Thread(() => runGnuplotClicks(surfaceGraphDataNames[i], masterDataLocation))
  // Code omitted for brevity.
}

这实际上正在做的是捕获变量i。但是,当线程开始执行时,i可能(甚至可能(增加数倍,直到其值现在为 5。IndexOutOfRangeException可能正在抛出,因为surfaceGraphDataNames没有 6 个插槽。没关系,您的线程没有使用您认为的i值。

要解决此问题,您需要创建一个特殊的捕获变量。

for (int i = 0; i < 5; i++)
{
  // Code omitted for brevity.
  int capture = i;
  new Thread(() => runGnuplotClicks(surfaceGraphDataNames[capture], masterDataLocation))
  // Code omitted for brevity.
}