zoukankan      html  css  js  c++  java
  • 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

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

  • 相关阅读:
    【动画】看动画轻松理解「Trie树」
    浅析HTTP/2的多路复用
    HTTPS 详解
    PHP写时复制(Copy On Write)
    golang 几种字符串的拼接方式
    正排索引和倒排索引简单介绍
    传值还是传引用
    lvs与nginx区别
    Docker运行操作系统环境(BusyBox&Alpine&Debian/Ubuntu&CentOS/Fedora)
    原创-thanos组件(聚合多个prometheus组件)原理介绍
  • 原文地址:https://www.cnblogs.com/cumt-cwp/p/8525222.html
Copyright © 2011-2022 走看看