使用列表作为线程启动例程的参数时出现超出范围的索引错误
本文关键字:范围 错误 索引 参数 线程 启动 例程 列表 | 更新日期: 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.
}