2

我正在编写一个 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++;
            }
4

3 回答 3

1

Looks like that you have to do something like this:

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

The reason is that when you use the variable i, it's not safe because when your i++, and the surfaceGraphDataNames has not been added with new item yet, the exception will throw because your Thread run nearly simultaneously.

Here is the context which leads to the exception:

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));
   //....
}

UPDATE

Looks like that the code above even can't work possibly because the i is increased before the actual ThreadStart is called. I think you can do this to make it safer:

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

Synchronization Attempt:

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();
}
于 2013-09-03T03:29:34.087 回答
0

I had a problem like this then I use Thread. I was sure that the index is not out of range and this situation not happened if I tried to stop by break-point and then continued. Try to use Task instead of Thread. It works

于 2013-09-03T03:25:48.350 回答
0

最明显的问题是您正在关闭循环变量。当你构造一个 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 的点。由于没有 6 个插槽,它可能IndexOutOfRangeException被抛出。surfaceGraphDataNames没关系,您的线程没有使用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.
}
于 2013-09-03T14:22:49.300 回答