C#代码中为数据库开启多线程进行数据更新引起的BUG总结。

    刚开始编程的时候,对多线程有着盲目的崇拜。遇到需要调用写的方法,就想用多线程来进行调用。结果这几天才发现了软件中的BUG,看来多线程也不是想用就能用的,用不好就会非常糟糕,导致一些莫名其名的BUG。

         我写了一个数据库的小例子,也验证了这个BUG是确实存在的。首先呢,我在数据库中创建了两个字段的表格,两个字段分别为M,N。其中M我设置为主键,并手动添加了从1到10的数据,再通过数据库更新的方式来对这10个数据进行更新。

             int[] array = new int[10] { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 };

        int[] array1 = new int[10] {21,22,23,24,25,26,27,28,29,30 };

        private void update(int i)

        {

            string sqlstr = null;

            sqlstr += "update Table_1 set N='"+array1[i]+ "' where M='"+ array[i] + "'";

            if (db.ExecDataBySql(sqlstr)>0)

            {

                MessageBox.Show("zhixingchengong!");

            }

        }

然后添加了一个Button控件来执行调用这个方法,如下:

  private void button1_Click(object sender, EventArgs e)

        {

           

            for (int i = 0; i < 10; i++)

            {

                if (i > 9) i = 9;

              new Task(()=> update(i)).Start()  ;

            }

}

之所以添加if (i > 9) i = 9;是因为程序莫名其妙的发生超出索引的异常,很是奇怪。虽然加了这个,程序依然会出现报错,无解。执行这个程序之后,发现数据只成功更新了3个数据。

最后才看到一段类似于我这样的多线程问题。

Lambda 表达式与被捕获变量

如我们所见,lambda 表达式是向线程传递数据的最强大的方法。然而必须小心,不要在启动线程之后误修改被捕获变量(captured variables)。例如,考虑下面的例子:

for (int i = 0; i < 10; i++)

  new Thread (() => Console.Write (i)).Start();

输出结果是不确定的!可能是这样0223557799。

问题在于变量i在整个循环中指向相同的内存地址。所以,每一个线程在调用Console.Write时,都在使用这个值在运行时会被改变的变量!

类似的问题在C# 4.0 in a Nutshell的第 8 章的 “Captured Variables” 有描述。这个问题与多线程没什么关系,而是和 C# 的捕获变量的规则有关(在for和foreach的场景下有时不是很理想)。

解决方法就是使用临时变量,如下所示:

for (int i = 0; i < 10; i++)

{

  int temp = i;

  new Thread (() => Console.Write (temp)).Start();

}

变量temp对于每一个循环迭代是局部的。所以,每一个线程会捕获一个不同的内存地址,从而不会产生问题。我们可以使用更为简单的代码来演示前面的问题:

string text = "t1";

Thread t1 = new Thread ( () => Console.WriteLine (text) );

 

text = "t2";

Thread t2 = new Thread ( () => Console.WriteLine (text) );

 

t1.Start();

t2.Start();

因为两个lambda表达式捕获了相同的text变量,t2会被打印两次:

t2

t2

 

看来线程也不是随便用的,我还是要慢慢搞懂每一个问题,让自己变得越来越强,程序越来越好看。

posted @ 2018-03-07 20:42  一念执着cumt  阅读(1976)  评论(0编辑  收藏  举报