断点跟一下,看到底是thread溢出还是table溢出你的table里面有10行吗?

解决方案 »

  1.   

    我把你的代码跑了一下,因为我没有定义table,所以我直接用的i.tostring来测试的
    没有问题
      

  2.   


    table.Rows[i]只是个例子演示给你们看而已,肯定不会错的断点调试过了,就是因为for循环的i+1后,前面代码里的i也会跟着改变了
      

  3.   

    数组是从0开始的,i=10超出范围了,应该是for (int i = 0; i < t1.Length-1; i++)
      

  4.   

    你的错误在于没有理解for循环和进程的关系,你以为最后i=10的时候不会走进去,其实这是个误区,i=10的时候只是不再产生新的进程,但你所有进程都使用一个全局的i,所以最终出现了string str = table.Rows[10]["str"].ToString(); 导致错误,改发就是把i作为参数传进去
      

  5.   

    你这个应该在 执行循环之前,table 是空的
      

  6.   

    原代码是这样:
     Thread[] thread = new Thread[dataGridViewX1.Rows.Count];
                    for (int i = 0; i < dataGridViewX1.Rows.Count; i++)
                    {
                         thread[i] = new Thread(new ThreadStart(delegate
                            {
                                DataTable tableWeb = SqliteHelper.GetWeblist(dataGridViewX1.Rows[i].Cells[1].Value.ToString());
                                DataTable tableCollect = new DataTable();
                                DataTable tableKeyword = SqliteHelper.GetKeyword(dataGridViewX1.Rows[i].Cells[5].Value.ToString());
                                DataTable tableArticle;
                                CollectRule rule = new CollectRule();
                                KeyCollect keyMethod = new KeyCollect();
                                ArrayList arrayList = new ArrayList();
                                Encoding enc = null;
                                //调试的时候这里的i也会报错
                                if (dataGridViewX1.Rows[i].Cells[2].Value.ToString().Contains("关键"))
                                {
                                    tableCollect = SqliteHelper.GetCollectRule("name", dataGridViewX1.Rows[i].Cells[2].Value.ToString());
                                    if (tableCollect.Rows[0]["encoding"].ToString().ToLower() == "gb2312")
                                    {
                                        enc = Encoding.GetEncoding("gb2312");
                                    }
                                    else if (tableCollect.Rows[0]["encoding"].ToString().ToLower() == "utf8")
                                    {
                                        enc = Encoding.UTF8;
                                    }
                                    arrayList = keyMethod.GetListUrl(tableCollect.Rows[0]["listUrl"].ToString(), tableKeyword.Rows[0]["value"].ToString(), tableCollect.Rows[0]["listTagStart"].ToString(), tableCollect.Rows[0]["listTagEnd"].ToString(), tableCollect.Rows[0]["suffix"].ToString(), 5, Convert.ToInt32(tableCollect.Rows[0]["cdouble"].ToString()), enc);
                                }
                                else if (dataGridViewX1.Rows[i].Cells[2].Value.ToString().Contains("自定义"))
                                {
                                    tableCollect = SqliteHelper.GetCollectRule("name", dataGridViewX1.Rows[i].Cells[2].Value.ToString());
                                    if (tableCollect.Rows[0]["encoding"].ToString().ToLower() == "gb2312")
                                    {
                                        enc = Encoding.GetEncoding("gb2312");
                                    }
                                    else if (tableCollect.Rows[0]["encoding"].ToString().ToLower() == "utf8")
                                    {
                                        enc = Encoding.UTF8;
                                    }
                                    arrayList = rule.GetListUrl(tableCollect.Rows[0]["listUrl"].ToString(), 22, tableCollect.Rows[0]["listTagStart"].ToString(), tableCollect.Rows[0]["listTagEnd"].ToString(), tableCollect.Rows[0]["suffix"].ToString(), Convert.ToInt32(tableCollect.Rows[0]["cdouble"].ToString()), enc);
                                }
                                for (int j = 0; j < arrayList.Count; j++)
                                {
                                    string title = string.Empty;
                                    string content = rule.GetContent(arrayList[j].ToString(), tableCollect.Rows[0]["contentTagStart"].ToString(), tableCollect.Rows[0]["contentTagEnd"].ToString(), tableCollect.Rows[0]["titleTagStart"].ToString(), tableCollect.Rows[0]["titleTagEnd"].ToString(), out title, enc);
                                    if (SqliteHelper.AddArticle(title, content, dataGridViewX1.Rows[0].Cells[4].Value.ToString()) == 1)
                                    {
                                        Cmd.Print("采集到1条标题为:《" + title + "》的数据",ConsoleColor.Green);
                                    }
                                    else
                                    {
                                        Cmd.Print("采集入库时失败标题为:《" + title + "》的数据",ConsoleColor.Red);
                                    }
                                }
                              
                                //这里的i会报错 索引超出.....
                                tableArticle = SqliteHelper.GetArticle(dataGridViewX1.Rows[i].Cells[4].Value.ToString());
                                Cmd.Print("采集完成,正在发布", ConsoleColor.Yellow);
                                for (int j = 0; j < tableArticle.Rows.Count; j++)
                                {
                                    PublishArticle(tableWeb.Rows[0]["cms"].ToString(), tableWeb.Rows[0]["url"].ToString(), tableWeb.Rows[0]["user"].ToString(), tableWeb.Rows[0]["pass"].ToString(),tableArticle.Rows[j]["title"].ToString(),tableArticle.Rows[j]["content"].ToString()); 
                                }
                            }));
                         thread[i].Start();
                         Thread.Sleep(1000);
                    }
      

  7.   

    或者 table 的行数 小于 10,或者dataGridViewX1.Rows的数量不到10
      

  8.   

    string str = table.Rows[9]["str"].ToString()
    只能到9,不会出现10 的
      

  9.   

    你这样说我也不理解了...
    i=10的时候,不是直接就走出去了么,为什么一部分代码走,另一部分代码不走?
    不会有这个情况的,i是值类型的
    我没说i不是值类型啊,但问题他没有把i作为参数传入线程,而是把i作为一个全局变量在使用,for循环停止的时候i=10,这时候不管你哪个线程,只要没有走完i都是10
      

  10.   

    dataGridViewX1.Rows和tale.row这些 行数之类的都没有问题的,但就是因为i=10  前面的i也变成10了,所以才报索引超过错误
      

  11.   

    谢谢回答,可能是我文字表达的不太清楚,只有你真正理解我的问题,您的意思是把i作为方法的参数传递过去,而不是赋值,对吗?
    不对的,i 的变化不会影响已经开始的线程
    但奇怪的是 i的变化的确影响了已经开始了的线程
      

  12.   

    用ParameterizedThreadStart委托代替ThreadStart委托
    并在匿名委托方法体中用委托参数代替i
    用thread[i].Start(i)来启动线程
      

  13.   

    谢谢回答,可能是我文字表达的不太清楚,只有你真正理解我的问题,您的意思是把i作为方法的参数传递过去,而不是赋值,对吗?
    是的
    但我写成把i作为方法的参数传递过去,还是会报这种索引的错,
     private DataTable TableArticle(int i)
            {
               return SqliteHelper.GetArticle(dataGridViewX1.Rows[i].Cells[4].Value.ToString());
            }
     tableArticle = TableArticle(i);
      

  14.   

    每次创建ThreadStart方法时把i拷贝传递进去,要不你外层循环结束的时候,里面的执行方法不一定结束了,就会出出现访问i=10的情况
      

  15.   

    Quote: 引用 21 楼 qaz952727 的回复:

    但我写成把i作为方法的参数传递过去,还是会报这种索引的错,
    Quote:

    我是说你重新写一个方法把,线程除外的代码都写那个方法里,然后那个方法有一个参数,然后启动线程的时候绑定那个方法,不是在线程里调用方法
      

  16.   


     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)
    {
          //你的代码
    }
      

  17.   

    Thread.Sleep(1000); 这句你看情况放里面放外面,和你业务有关
      

  18.   

    不太明白,请问怎么把i拷贝传递进去?是 int c=i; 这样吗?以前试过了  因为是在for循环里面赋值的 所以i=10  ,c也=10
      

  19.   

    谢谢,行了,我试了下  我下面的两个写法都可以,没有报错,但就是不知道这两种写法有什么区别,利弊?
    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);
                    }
      

  20.   

    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类型,可以传任何数据)传到线程要执行的委托
      

  21.   

    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 线程,并没有释放,线程多了 卡爆你
      

  22.   

    这是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);
                }
      

  23.   

    多线程操作资源,需要设置枷锁,string str = table.Rows[i]["str"].ToString();如果不想加锁想访问的话需要先比较i是否在 table.Rows.Count范围之内
      

  24.   


    也就是说这是4.0的BUG?,4.5 就不会因为全局变量改变而影响其他, string str = table.Rows[index]["str"].ToString();  像这个4.5把他独立分了一个包去执行,不受其他的干扰,是这样么
      

  25.   

    4.0之前也没有bug。而且这也跟“加锁”扯不上关系。犯不着为了在自己家上厕所也要到工商局去登记排队(加锁)。对于多个线程共享的输入变量,你就应该“先复制引用,再使用引用副本”。就好像 int index = i;做的一样。如果说4.5之类的给你自动将参数做了栈复制和转换,那只能说它可能放弃类之前“可以改变共享变量”这个特点。丢掉了之前的特性,而默认地改变为现在的新特性。
      

  26.   


    同时访问table怎么就“没问题”?如果一个线程把table的rows给清空了,那么其它的线程线程运行时也会出错。但是这里如果不可能出现这种现象,那么就无需防止它!因此这类编程问题不是以“洁癖”为美,无法给个规定还能精当地运行,而是要随着测试的变化而重构代码的。我们其实往往需要多线程是可以高效率地修改共享变量,例如有时候我们可能用来统计运行进度(但是并不要求精确)。只不过反过来说,你也同时应该知道修改共享变量 i 的后果。
      

  27.   

    多线程代码貌似没问题,有些人看见多线程就说要加锁,
    其实楼主的代码不是异步额.
    问题应该处在
     string str = table.Rows[i]["str"].ToString();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 线程,并没有释放,线程多了 卡爆你
    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
    请按任意键继续. . .
      

  28.   


    也就是说这是4.0的BUG?,4.5 就不会因为全局变量改变而影响其他, string str = table.Rows[index]["str"].ToString();  像这个4.5把他独立分了一个包去执行,不受其他的干扰,是这样么
    这不是bug,而是一种设计方式,左手右手的选择而已,4.5更改了设计。
      

  29.   

    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 线程,并没有释放,线程多了 卡爆你
    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
    请按任意键继续. . .
    怎么说呢,只是他写了睡眠而已,1000ms保证了子线程代码执行完成
      

  30.   


    同时访问table怎么就“没问题”?如果一个线程把table的rows给清空了,那么其它的线程线程运行时也会出错。但是这里如果不可能出现这种现象,那么就无需防止它!因此这类编程问题不是以“洁癖”为美,无法给个规定还能精当地运行,而是要随着测试的变化而重构代码的。我们其实往往需要多线程是可以高效率地修改共享变量,例如有时候我们可能用来统计运行进度(但是并不要求精确)。只不过反过来说,你也同时应该知道修改共享变量 i 的后果。
    我的意思是说同时访问table是读,不是改
      

  31.   

    既然是索引报错就是索引超了,这个很明确。
    而且你发现了没有你用的多线程,而且你委托套在循环里,你以为没启动委托,但是因为循环太快,你i=10的时候,最后一个委托还没托出去,就把值i变成了10,所以就产生了你所谓的错误,i变成了10,其实你委托完全可以写到外面去啊,直接赋值多好?