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

Wesley13
• 阅读 602

    刚开始编程的时候,对多线程有着盲目的崇拜。遇到需要调用写的方法,就想用多线程来进行调用。结果这几天才发现了软件中的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

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

点赞
收藏
评论区
推荐文章
blmius blmius
3年前
MySQL:[Err] 1292 - Incorrect datetime value: ‘0000-00-00 00:00:00‘ for column ‘CREATE_TIME‘ at row 1
文章目录问题用navicat导入数据时,报错:原因这是因为当前的MySQL不支持datetime为0的情况。解决修改sql\mode:sql\mode:SQLMode定义了MySQL应支持的SQL语法、数据校验等,这样可以更容易地在不同的环境中使用MySQL。全局s
皕杰报表之UUID
​在我们用皕杰报表工具设计填报报表时,如何在新增行里自动增加id呢?能新增整数排序id吗?目前可以在新增行里自动增加id,但只能用uuid函数增加UUID编码,不能新增整数排序id。uuid函数说明:获取一个UUID,可以在填报表中用来创建数据ID语法:uuid()或uuid(sep)参数说明:sep布尔值,生成的uuid中是否包含分隔符'',缺省为
待兔 待兔
5个月前
手写Java HashMap源码
HashMap的使用教程HashMap的使用教程HashMap的使用教程HashMap的使用教程HashMap的使用教程22
Jacquelyn38 Jacquelyn38
3年前
2020年前端实用代码段,为你的工作保驾护航
有空的时候,自己总结了几个代码段,在开发中也经常使用,谢谢。1、使用解构获取json数据let jsonData  id: 1,status: "OK",data: 'a', 'b';let  id, status, data: number   jsonData;console.log(id, status, number )
Stella981 Stella981
3年前
Django之Django模板
1、问:html页面从数据库中读出DateTimeField字段时,显示的时间格式和数据库中存放的格式不一致,比如数据库字段内容为2012082616:00:00,但是页面显示的却是Aug.26,2012,4p.m.答:为了页面和数据库中显示一致,需要在页面格式化时间,需要添加<td{{dayrecord.p\_time|date:
Stella981 Stella981
3年前
Android So动态加载 优雅实现与原理分析
背景:漫品Android客户端集成适配转换功能(基于目标识别(So库35M)和人脸识别库(5M)),导致apk体积50M左右,为优化客户端体验,决定实现So文件动态加载.!(https://oscimg.oschina.net/oscnet/00d1ff90e4b34869664fef59e3ec3fdd20b.png)点击上方“蓝字”关注我
Easter79 Easter79
3年前
Twitter的分布式自增ID算法snowflake (Java版)
概述分布式系统中,有一些需要使用全局唯一ID的场景,这种时候为了防止ID冲突可以使用36位的UUID,但是UUID有一些缺点,首先他相对比较长,另外UUID一般是无序的。有些时候我们希望能使用一种简单一些的ID,并且希望ID能够按照时间有序生成。而twitter的snowflake解决了这种需求,最初Twitter把存储系统从MySQL迁移
Stella981 Stella981
3年前
Django中Admin中的一些参数配置
设置在列表中显示的字段,id为django模型默认的主键list_display('id','name','sex','profession','email','qq','phone','status','create_time')设置在列表可编辑字段list_editable
Wesley13 Wesley13
3年前
35岁是技术人的天花板吗?
35岁是技术人的天花板吗?我非常不认同“35岁现象”,人类没有那么脆弱,人类的智力不会说是35岁之后就停止发展,更不是说35岁之后就没有机会了。马云35岁还在教书,任正非35岁还在工厂上班。为什么技术人员到35岁就应该退役了呢?所以35岁根本就不是一个问题,我今年已经37岁了,我发现我才刚刚找到自己的节奏,刚刚上路。
Python进阶者 Python进阶者
11个月前
Excel中这日期老是出来00:00:00,怎么用Pandas把这个去除
大家好,我是皮皮。一、前言前几天在Python白银交流群【上海新年人】问了一个Pandas数据筛选的问题。问题如下:这日期老是出来00:00:00,怎么把这个去除。二、实现过程后来【论草莓如何成为冻干莓】给了一个思路和代码如下:pd.toexcel之前把这