• 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

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

  • 相关阅读:
    C#学习笔记-代理模式
    SqlDbx连接oracle
    C# 连接oracle,用32位client和64位Client,可能导致结果不同
    PHP&Java 调用C#的WCF
    DevExpress GridControl 控件二表连动
    SSAS 非重复计数
    Corn 表达式
    C# 实现Tree,包含parentId和children
    jsfiddle.net上的记录
    【慕课网实战】Spark Streaming实时流处理项目实战笔记十二之铭文升级版
  • 原文地址:https://www.cnblogs.com/cumt-cwp/p/8525222.html
Copyright © 2020-2023  润新知