C#多线程for循环报索引超出范围的错误?

qaz952727 2014-04-25 03:59:26
我用for循环去开多个线程,每个线程里面都有一些变量用索引的方式赋值,比如string str=dataGridViewX1.Rows[i].Tostring()这样,但每次运行到那里都会报索引超出范围的错误,请问这是为什么?如何解决呢?
线程启动后,这个线程不是独立的吗?为什么会因为for循环的i+1后,前面代码里的i也会跟着改变了?
   Thread[] t1=new Thread[10];
for (int i = 0; i < t1.Length; i++)
{
t1[i] = new Thread(new ThreadStart(delegate
{

//
//Do something......
//Do something......
//Do something......
//

//当循环到最后一次的时候,i会变成10,那for循环就肯定不会再执行下去,但这里的i也会变成10,就会报索引超出数组界限的错误。
//而且在for循环里面的所有的i都会报这个错误。
string str = table.Rows[i]["str"].ToString();
}
));
t1[i].Start();
Thread.Sleep(1000);
}
...全文
1440 44 打赏 收藏 转发到动态 举报
写回复
用AI写文章
44 条回复
切换为时间正序
请发表友善的回复…
发表回复
白沙水 2016-10-09
  • 打赏
  • 举报
回复 2
引用 35 楼 sp1234 的回复:
4.0之前也没有bug。 而且这也跟“加锁”扯不上关系。犯不着为了在自己家上厕所也要到工商局去登记排队(加锁)。 对于多个线程共享的输入变量,你就应该“先复制引用,再使用引用副本”。就好像
int index = i;
做的一样。 如果说4.5之类的给你自动将参数做了栈复制和转换,那只能说它可能放弃类之前“可以改变共享变量”这个特点。丢掉了之前的特性,而默认地改变为现在的新特性。
这个方法试了下,有用.
qaz952727 2014-04-28
  • 打赏
  • 举报
回复
谢谢大家的回答,答案在得分楼层
xsan_3 2014-04-28
  • 打赏
  • 举报
回复
既然是索引报错就是索引超了,这个很明确。 而且你发现了没有你用的多线程,而且你委托套在循环里,你以为没启动委托,但是因为循环太快,你i=10的时候,最后一个委托还没托出去,就把值i变成了10,所以就产生了你所谓的错误,i变成了10,其实你委托完全可以写到外面去啊,直接赋值多好?
黑子大哥 2014-04-28
  • 打赏
  • 举报
回复
引用 36 楼 sp1234 的回复:
[quote=引用 31 楼 qhttl 的回复:] [LZ 其实是你这里跨线程了,如果没猜错,你报错的地方是string str = table.Rows[i]["str"].ToString(); 这句话,for语句完全正确,别相信上边说的什么Length-1 超出索引的错误是因为table表中一共没有10行导致或者说没有列名叫str的 你这样写而且不安全,因为跨线程,多个线程同时访问一个table没问题,但是同时访问变化的 i 变量会有问题的 比如说,循环第一次,当线程开启的时候 循环第二次(此时已经执行i++) 当执行string str = table.Rows[i]["str"].ToString();这时候i=1,取到的是第二行,或者说当循环都循环到第10次了,第一次的线程才开始执行,那i就是9了,完全不对 还有一个问题,你一直在new 线程,并没有释放,线程多了 卡爆你
同时访问table怎么就“没问题”?如果一个线程把table的rows给清空了,那么其它的线程线程运行时也会出错。 但是这里如果不可能出现这种现象,那么就无需防止它!因此这类编程问题不是以“洁癖”为美,无法给个规定还能精当地运行,而是要随着测试的变化而重构代码的。 我们其实往往需要多线程是可以高效率地修改共享变量,例如有时候我们可能用来统计运行进度(但是并不要求精确)。只不过反过来说,你也同时应该知道修改共享变量 i 的后果。 [/quote] 我的意思是说同时访问table是读,不是改
黑子大哥 2014-04-28
  • 打赏
  • 举报
回复 1
引用 37 楼 sj178220709 的回复:
多线程代码貌似没问题,有些人看见多线程就说要加锁, 其实楼主的代码不是异步额. 问题应该处在 string str = table.Rows[i]["str"].ToString(); [quote=引用 31 楼 qhttl 的回复:] [quote=引用 楼主 qaz952727 的回复:] 我用for循环去开多个线程,每个线程里面都有一些变量用索引的方式赋值,比如string str=dataGridViewX1.Rows[i].Tostring()这样,但每次运行到那里都会报索引超出范围的错误,请问这是为什么?如何解决呢? 线程启动后,这个线程不是独立的吗?为什么会因为for循环的i+1后,前面代码里的i也会跟着改变了?
   Thread[] t1=new Thread[10];
            for (int i = 0; i < t1.Length; i++)
            {
                t1[i] = new Thread(new ThreadStart(delegate
                    {

                        //
                        //Do something......
                        //Do something......
                        //Do something......
                        //

                        //当循环到最后一次的时候,i会变成10,那for循环就肯定不会再执行下去,但这里的i也会变成10,就会报索引超出数组界限的错误。
                        //而且在for循环里面的所有的i都会报这个错误。
                        string str = table.Rows[i]["str"].ToString();
                    }
                    ));
                t1[i].Start();
                Thread.Sleep(1000);
            }
LZ 其实是你这里跨线程了,如果没猜错,你报错的地方是string str = table.Rows[i]["str"].ToString(); 这句话,for语句完全正确,别相信上边说的什么Length-1 超出索引的错误是因为table表中一共没有10行导致或者说没有列名叫str的 你这样写而且不安全,因为跨线程,多个线程同时访问一个table没问题,但是同时访问变化的 i 变量会有问题的 比如说,循环第一次,当线程开启的时候 循环第二次(此时已经执行i++) 当执行string str = table.Rows[i]["str"].ToString();这时候i=1,取到的是第二行,或者说当循环都循环到第10次了,第一次的线程才开始执行,那i就是9了,完全不对 还有一个问题,你一直在new 线程,并没有释放,线程多了 卡爆你 [/quote] 31楼的朋友,这里没有涉及线程同步哦, t1[i].Start(); Thread.Sleep(1000); 线程启动是在主线程里,但是 Thread.Sleep(1000);也阻塞的是主线程,

  Thread[] t1 = new Thread[10];

            for (int i = 0; i < t1.Length; i++)
            {
                t1[i] = new Thread(() =>
                {
                    Console.WriteLine("the i is " + i+",thread is :"+Thread.CurrentThread.Name);
                })
                {
                    Name = "thread"+i
                };
                   
                t1[i].Start();
                Thread.Sleep(1000);
            }
结果: the i is 0,thread is :thread0 the i is 1,thread is :thread1 the i is 2,thread is :thread2 the i is 3,thread is :thread3 the i is 4,thread is :thread4 the i is 5,thread is :thread5 the i is 6,thread is :thread6 the i is 7,thread is :thread7 the i is 8,thread is :thread8 the i is 9,thread is :thread9 请按任意键继续. . .[/quote] 怎么说呢,只是他写了睡眠而已,1000ms保证了子线程代码执行完成
  • 打赏
  • 举报
回复
多线程代码貌似没问题,有些人看见多线程就说要加锁, 其实楼主的代码不是异步额. 问题应该处在 string str = table.Rows[i]["str"].ToString();
引用 31 楼 qhttl 的回复:
[quote=引用 楼主 qaz952727 的回复:] 我用for循环去开多个线程,每个线程里面都有一些变量用索引的方式赋值,比如string str=dataGridViewX1.Rows[i].Tostring()这样,但每次运行到那里都会报索引超出范围的错误,请问这是为什么?如何解决呢? 线程启动后,这个线程不是独立的吗?为什么会因为for循环的i+1后,前面代码里的i也会跟着改变了?
   Thread[] t1=new Thread[10];
            for (int i = 0; i < t1.Length; i++)
            {
                t1[i] = new Thread(new ThreadStart(delegate
                    {

                        //
                        //Do something......
                        //Do something......
                        //Do something......
                        //

                        //当循环到最后一次的时候,i会变成10,那for循环就肯定不会再执行下去,但这里的i也会变成10,就会报索引超出数组界限的错误。
                        //而且在for循环里面的所有的i都会报这个错误。
                        string str = table.Rows[i]["str"].ToString();
                    }
                    ));
                t1[i].Start();
                Thread.Sleep(1000);
            }
LZ 其实是你这里跨线程了,如果没猜错,你报错的地方是string str = table.Rows[i]["str"].ToString(); 这句话,for语句完全正确,别相信上边说的什么Length-1 超出索引的错误是因为table表中一共没有10行导致或者说没有列名叫str的 你这样写而且不安全,因为跨线程,多个线程同时访问一个table没问题,但是同时访问变化的 i 变量会有问题的 比如说,循环第一次,当线程开启的时候 循环第二次(此时已经执行i++) 当执行string str = table.Rows[i]["str"].ToString();这时候i=1,取到的是第二行,或者说当循环都循环到第10次了,第一次的线程才开始执行,那i就是9了,完全不对 还有一个问题,你一直在new 线程,并没有释放,线程多了 卡爆你 [/quote] 31楼的朋友,这里没有涉及线程同步哦, t1[i].Start(); Thread.Sleep(1000); 线程启动是在主线程里,但是 Thread.Sleep(1000);也阻塞的是主线程,

  Thread[] t1 = new Thread[10];

            for (int i = 0; i < t1.Length; i++)
            {
                t1[i] = new Thread(() =>
                {
                    Console.WriteLine("the i is " + i+",thread is :"+Thread.CurrentThread.Name);
                })
                {
                    Name = "thread"+i
                };
                   
                t1[i].Start();
                Thread.Sleep(1000);
            }
结果: the i is 0,thread is :thread0 the i is 1,thread is :thread1 the i is 2,thread is :thread2 the i is 3,thread is :thread3 the i is 4,thread is :thread4 the i is 5,thread is :thread5 the i is 6,thread is :thread6 the i is 7,thread is :thread7 the i is 8,thread is :thread8 the i is 9,thread is :thread9 请按任意键继续. . .
  • 打赏
  • 举报
回复
引用 31 楼 qhttl 的回复:
[LZ 其实是你这里跨线程了,如果没猜错,你报错的地方是string str = table.Rows[i]["str"].ToString(); 这句话,for语句完全正确,别相信上边说的什么Length-1 超出索引的错误是因为table表中一共没有10行导致或者说没有列名叫str的 你这样写而且不安全,因为跨线程,多个线程同时访问一个table没问题,但是同时访问变化的 i 变量会有问题的 比如说,循环第一次,当线程开启的时候 循环第二次(此时已经执行i++) 当执行string str = table.Rows[i]["str"].ToString();这时候i=1,取到的是第二行,或者说当循环都循环到第10次了,第一次的线程才开始执行,那i就是9了,完全不对 还有一个问题,你一直在new 线程,并没有释放,线程多了 卡爆你
同时访问table怎么就“没问题”?如果一个线程把table的rows给清空了,那么其它的线程线程运行时也会出错。 但是这里如果不可能出现这种现象,那么就无需防止它!因此这类编程问题不是以“洁癖”为美,无法给个规定还能精当地运行,而是要随着测试的变化而重构代码的。 我们其实往往需要多线程是可以高效率地修改共享变量,例如有时候我们可能用来统计运行进度(但是并不要求精确)。只不过反过来说,你也同时应该知道修改共享变量 i 的后果。
  • 打赏
  • 举报
回复
4.0之前也没有bug。 而且这也跟“加锁”扯不上关系。犯不着为了在自己家上厕所也要到工商局去登记排队(加锁)。 对于多个线程共享的输入变量,你就应该“先复制引用,再使用引用副本”。就好像
int index = i;
做的一样。 如果说4.5之类的给你自动将参数做了栈复制和转换,那只能说它可能放弃类之前“可以改变共享变量”这个特点。丢掉了之前的特性,而默认地改变为现在的新特性。
qaz952727 2014-04-27
  • 打赏
  • 举报
回复
引用 32 楼 wddw1986 的回复:
这是4.0以及之前版本的闭包的设计问题。4.5之后这个问题做了修改 之前的版本可以使用。

for (int i = 0; i < t1.Length; i++)
            {
                int index = i;
                t1[i] = new Thread(new ThreadStart(delegate
                    {
 
                        //
                        //Do something......
                        //Do something......
                        //Do something......
                        //
 
                        //当循环到最后一次的时候,i会变成10,那for循环就肯定不会再执行下去,但这里的i也会变成10,就会报索引超出数组界限的错误。
                        //而且在for循环里面的所有的i都会报这个错误。
                        string str = table.Rows[index]["str"].ToString();
                    }
                    ));
                t1[i].Start();
                Thread.Sleep(1000);
            }
也就是说这是4.0的BUG?,4.5 就不会因为全局变量改变而影响其他, string str = table.Rows[index]["str"].ToString(); 像这个4.5把他独立分了一个包去执行,不受其他的干扰,是这样么
X_Craft 2014-04-27
  • 打赏
  • 举报
回复
是线程安全的调不
cheng2005 2014-04-27
  • 打赏
  • 举报
回复
引用 34 楼 qaz952727 的回复:
[quote=引用 32 楼 wddw1986 的回复:] 这是4.0以及之前版本的闭包的设计问题。4.5之后这个问题做了修改 之前的版本可以使用。

for (int i = 0; i < t1.Length; i++)
            {
                int index = i;
                t1[i] = new Thread(new ThreadStart(delegate
                    {
 
                        //
                        //Do something......
                        //Do something......
                        //Do something......
                        //
 
                        //当循环到最后一次的时候,i会变成10,那for循环就肯定不会再执行下去,但这里的i也会变成10,就会报索引超出数组界限的错误。
                        //而且在for循环里面的所有的i都会报这个错误。
                        string str = table.Rows[index]["str"].ToString();
                    }
                    ));
                t1[i].Start();
                Thread.Sleep(1000);
            }
也就是说这是4.0的BUG?,4.5 就不会因为全局变量改变而影响其他, string str = table.Rows[index]["str"].ToString(); 像这个4.5把他独立分了一个包去执行,不受其他的干扰,是这样么[/quote] 这不是bug,而是一种设计方式,左手右手的选择而已,4.5更改了设计。
cheng2005 2014-04-26
  • 打赏
  • 举报
回复
这是4.0以及之前版本的闭包的设计问题。4.5之后这个问题做了修改 之前的版本可以使用。

for (int i = 0; i < t1.Length; i++)
            {
                int index = i;
                t1[i] = new Thread(new ThreadStart(delegate
                    {
 
                        //
                        //Do something......
                        //Do something......
                        //Do something......
                        //
 
                        //当循环到最后一次的时候,i会变成10,那for循环就肯定不会再执行下去,但这里的i也会变成10,就会报索引超出数组界限的错误。
                        //而且在for循环里面的所有的i都会报这个错误。
                        string str = table.Rows[index]["str"].ToString();
                    }
                    ));
                t1[i].Start();
                Thread.Sleep(1000);
            }
黑子大哥 2014-04-26
  • 打赏
  • 举报
回复
引用 楼主 qaz952727 的回复:
我用for循环去开多个线程,每个线程里面都有一些变量用索引的方式赋值,比如string str=dataGridViewX1.Rows[i].Tostring()这样,但每次运行到那里都会报索引超出范围的错误,请问这是为什么?如何解决呢? 线程启动后,这个线程不是独立的吗?为什么会因为for循环的i+1后,前面代码里的i也会跟着改变了?
   Thread[] t1=new Thread[10];
            for (int i = 0; i < t1.Length; i++)
            {
                t1[i] = new Thread(new ThreadStart(delegate
                    {

                        //
                        //Do something......
                        //Do something......
                        //Do something......
                        //

                        //当循环到最后一次的时候,i会变成10,那for循环就肯定不会再执行下去,但这里的i也会变成10,就会报索引超出数组界限的错误。
                        //而且在for循环里面的所有的i都会报这个错误。
                        string str = table.Rows[i]["str"].ToString();
                    }
                    ));
                t1[i].Start();
                Thread.Sleep(1000);
            }
LZ 其实是你这里跨线程了,如果没猜错,你报错的地方是string str = table.Rows[i]["str"].ToString(); 这句话,for语句完全正确,别相信上边说的什么Length-1 超出索引的错误是因为table表中一共没有10行导致或者说没有列名叫str的 你这样写而且不安全,因为跨线程,多个线程同时访问一个table没问题,但是同时访问变化的 i 变量会有问题的 比如说,循环第一次,当线程开启的时候 循环第二次(此时已经执行i++) 当执行string str = table.Rows[i]["str"].ToString();这时候i=1,取到的是第二行,或者说当循环都循环到第10次了,第一次的线程才开始执行,那i就是9了,完全不对 还有一个问题,你一直在new 线程,并没有释放,线程多了 卡爆你
lis2012 2014-04-26
  • 打赏
  • 举报
回复
多线程操作资源,需要设置枷锁,string str = table.Rows[i]["str"].ToString();如果不想加锁想访问的话需要先比较i是否在 table.Rows.Count范围之内
  • 打赏
  • 举报
回复
Thread[] t1=new Thread[10]; for (int i = 0; i < t1.Length; i++) { t1[i] = new Thread(new ThreadStart(delegate(object state) { // //Do something...... //Do something...... //Do something...... // //当循环到最后一次的时候,i会变成10,那for循环就肯定不会再执行下去,但这里的i也会变成10,就会报索引超出数组界限的错误。 //而且在for循环里面的所有的i都会报这个错误。 int index = (int)state; string str = table.Rows[index]["str"].ToString(); } )); t1[i].Start(i); Thread.Sleep(1000); } 原因是线程启动需要时间,而for循环执行去很快,这样就会导致线程里的代码执行的时候,外部变量i早已被for改掉了 你可以使用传参的重载,将每次循环的i值(object类型,可以传任何数据)传到线程要执行的委托
qaz952727 2014-04-25
  • 打赏
  • 举报
回复
引用 24 楼 xdashewan 的回复:

 Thread[] thread = new Thread[dataGridViewX1.Rows.Count];
                for (int i = 0; i < dataGridViewX1.Rows.Count; i++)
                {
                     thread[i] = new Thread(new ParameterizedThreadStart(DoYourWork));
                     thread[i].Start(i);
                     Thread.Sleep(1000);
                }

public void DoYourWork(object  index)
{
      //你的代码
}
谢谢,行了,我试了下 我下面的两个写法都可以,没有报错,但就是不知道这两种写法有什么区别,利弊?
Thread[] thread = new Thread[dataGridViewX1.Rows.Count];
                for (int i = 0; i < dataGridViewX1.Rows.Count; i++)
                {
                    //以前一直用new Thread(new ThreadStart(delegate{} 这种写法
                    thread[i] = new Thread(new ThreadStart(delegate
                       {
                           ThreadMethod(i);
                       }));
                    thread[i].Start();
                    Thread.Sleep(1000);
                }
                for (int i = 0; i < dataGridViewX1.Rows.Count; i++)
                {
                    //现在用new ParameterizedThreadStart(ThreadMethod),不知这两个有什么区别,利弊?感觉效果都一样
                    thread[i] = new Thread(new ParameterizedThreadStart(ThreadMethod));
                    thread[i].Start(i);
                    Thread.Sleep(1000);
                }
於黾 2014-04-25
  • 打赏
  • 举报
回复
有点理解了 因为是代码直接当成线程在跑,没有封装成函数,所以i并不是传递进去的,而是一直都在变
qaz952727 2014-04-25
  • 打赏
  • 举报
回复
引用 22 楼 hanxuetaotao2 的回复:
每次创建ThreadStart方法时把i拷贝传递进去,要不你外层循环结束的时候,里面的执行方法不一定结束了,就会出出现访问i=10的情况
不太明白,请问怎么把i拷贝传递进去?是 int c=i; 这样吗?以前试过了 因为是在for循环里面赋值的 所以i=10 ,c也=10
xdashewan 2014-04-25
  • 打赏
  • 举报
回复
Thread.Sleep(1000); 这句你看情况放里面放外面,和你业务有关
xdashewan 2014-04-25
  • 打赏
  • 举报
回复

 Thread[] thread = new Thread[dataGridViewX1.Rows.Count];
                for (int i = 0; i < dataGridViewX1.Rows.Count; i++)
                {
                     thread[i] = new Thread(new ParameterizedThreadStart(DoYourWork));
                     thread[i].Start(i);
                     Thread.Sleep(1000);
                }

public void DoYourWork(object  index)
{
      //你的代码
}
加载更多回复(23)

110,574

社区成员

发帖
与我相关
我的任务
社区描述
.NET技术 C#
社区管理员
  • C#
  • Web++
  • by_封爱
加入社区
  • 近7日
  • 近30日
  • 至今
社区公告

让您成为最强悍的C#开发者

试试用AI创作助手写篇文章吧