我正在编写一个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++;
}
答案 0 :(得分:1)
看起来你必须做这样的事情:
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));
//....
}
看起来上面的代码甚至无法工作,因为i
在调用实际ThreadStart
之前增加了。我认为你可以做到这一点让它更安全:
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();
}
答案 1 :(得分:0)
我有这样的问题然后我使用Thread。我确信指数并没有超出范围,如果我试图以突破点停止然后继续,这种情况就不会发生。 尝试使用Task而不是Thread。它的工作原理
答案 2 :(得分:0)
最明显的问题是你是closing over the loop variable。构造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.
}