当前位置:编程学习 > C#/ASP.NET >>

请教多线程操作同一个数据表时,怎么避免表死锁

最近有一个数据采集的项目,要求可以多路并发,遇到多线程操作同一个数据表的问题,经常发生表死锁,求各位大侠帮忙,大约有20-40个进程实例化读取类,类中Receive方法接收数据并存储
上代码

public class clsTranslateObj
    {
        private void Receive()
        { 
            int lenReceive = 0;
            byte[] receivedByte = new byte[BUFFER_SIZE];       //接收返回字节数组

            _rtnStr = string.Empty;

            while (_flgConnect)
            {
                _rtnStr = string.Empty;

                try
                {
                    lenReceive = _translateClient.Receive(receivedByte, 0, BUFFER_SIZE, SocketFlags.None);

                    for (int i = 0; i < lenReceive; i++)
                    {
                        if (Convert.ToString(receivedByte[i], 16).Length == 1)
                            _rtnStr += ("0" + Convert.ToString(receivedByte[i], 16).ToString());
                        else if (Convert.ToString(receivedByte[i], 16).Length == 2)
                            _rtnStr += Convert.ToString(receivedByte[i], 16).ToString();
                    }

                    //接收成功处理
                    Console.WriteLine(_switchcode + " receive:" + lenReceive.ToString());
                    
                    for(int j = 0; j < _rtnStr.Length; j+2)
                    {
                        sqlCmd = "insert into tagvalue(tagdatetime, tagcode, tagval) values(?tagdatetime, ?tagcode, ?tagval)";
                        
                        //tagdatetime = DateTime.Now;
                        //tagcode = 实验室号;
                        //tagval = _rtnStr.Substring(j, 2);
                    }

                }
                catch(Exception ex)
                {
                    Console.WriteLine(_switchcode + " Receive() error:" + ex.Message);
                }
            }
        }
    }


mysql中倒是有解决办法

sqlCmd = "insert DELAYED into tagvalue(tagdatetime, tagcode, tagval) values(?tagdatetime, ?tagcode, ?tagval)";


DELAYED 使用延迟插入操作DELAYED调节符应用于INSERT和REPLACE语句。当DELAYED插入操作到达的时候,服务器把数据行放入一个队列中,并立即给客户端返回一个状态信息,这样客户端就可以在数据表被真正地插入记录之前继续进行操作了。如果读取者从该数据表中读取数据,队列中的数据就会被保持着,直到没有读取者为止。接着服务器开始插入延迟数据行(delayed-row)队列中的数据行。在插入操作的同时,服务器还要检查是否有新的读取请求到达和等待。如果有,延迟数据行队列就被挂起,允许读取者继续操作。当没有读取者的时候,服务器再次开始插入延迟的数据行。 这个过程一直进行,直到队列空了为止。


请问各位大侠,像其他Oracle、MSSQL等数据库怎么处理啊,急求啊,望各位赐教 --------------------编程问答-------------------- 死锁是什么概念?表现出哪些问题? 真心求教! --------------------编程问答--------------------
引用 1 楼  的回复:
死锁是什么概念?表现出哪些问题? 真心求教!


一个线程
lock(A){

sleep(1000);

lock(B){
.....
.....
}
}



一个线程
lock(B){
sleep(500);
lock(A){
.....
.....
}
}

两个线程启动

最终死锁

谁也无法继续运行

具体去看书吧

有个四条件 --------------------编程问答-------------------- 为什么不是一个线程操作数据库部分, 而是多个线程一起操作...
就算不死锁,数据也有有问题吧 --------------------编程问答-------------------- --------------------编程问答-------------------- --------------------编程问答-------------------- 没有人遇到过么 --------------------编程问答-------------------- 这样的设计不太合理

通常来说,是多个线程将数据插入一个队列,然后有个专门的线程来根据队列中的数据对数据库操作,这样就不会有问题 --------------------编程问答--------------------
引用 7 楼  的回复:
这样的设计不太合理

通常来说,是多个线程将数据插入一个队列,然后有个专门的线程来根据队列中的数据对数据库操作,这样就不会有问题

那请问队列的访问控制呢,多线程插入,还有存储线程读取、删除 --------------------编程问答-------------------- 由一个线程读写表, 其它线程则是处理数据 --------------------编程问答-------------------- 你可以用多线程把采集到的数据存储到一个队列里(存入队列的时候要锁住),然后再启动一个线程定时从队列把数据存入数据库,这样就不会有并发问题 --------------------编程问答--------------------
引用 2 楼  的回复:
引用 1 楼 的回复:

死锁是什么概念?表现出哪些问题? 真心求教!


一个线程
lock(A){

sleep(1000);

lock(B){
.....
.....
}
}



一个线程
lock(B){
sleep(500);
lock(A){
.....
.....
}
}

两个线程启动

最终死锁

谁也无法继续运行……

非常感谢! --------------------编程问答-------------------- 能否说的再具体点啊 --------------------编程问答-------------------- 不应该啊,单纯的多线程insert不可能造成表锁的。
你关键部分的入库操作的代码没有啊,严重怀疑是反复开关数据库连接造成的数据连接池响应不能,导致失败。 --------------------编程问答-------------------- 怎么没看到你线程在那里呀,是不是你用主线程,主线程死锁了。 --------------------编程问答--------------------
引用 13 楼  的回复:
不应该啊,单纯的多线程insert不可能造成表锁的。
你关键部分的入库操作的代码没有啊,严重怀疑是反复开关数据库连接造成的数据连接池响应不能,导致失败。


public static int SaveOnceData(DateTime tagdt, string tagcd, string tagvl)
        {
            try
            {
                if (mc.State != System.Data.ConnectionState.Open)
                    mc.Open();

                int rtn = 0;

                string sqlCmd = "insert DELAYED into tagonce(tagdatetime, tagcode, tagval) values(?tagdatetime, ?tagcode, ?tagval)";

                if (mc != null)
                {
                    MySqlCommand cmd = new MySqlCommand(sqlCmd, mc);
                    cmd.CommandTimeout = 5;

                    cmd.Parameters.Add("?tagdatetime", MySqlDbType.Timestamp);
                    cmd.Parameters["?tagdatetime"].Value = tagdt;

                    cmd.Parameters.Add("?tagcode", MySqlDbType.Text);
                    cmd.Parameters["?tagcode"].Value = tagcd;

                    cmd.Parameters.Add("?tagval", MySqlDbType.Text);
                    cmd.Parameters["?tagval"].Value = tagvl;

                    rtn = cmd.ExecuteNonQuery();
                }

                return rtn;
            }
            catch (Exception e)
            {
                return 0;
            }
        }
--------------------编程问答-------------------- 用 lock --------------------编程问答--------------------
引用 16 楼  的回复:
用 lock

能详细一点么 --------------------编程问答-------------------- 学习了,正好解决我的一个问题 --------------------编程问答-------------------- 可用个池,每个线程更新池中的数据,最后更新数据库。 --------------------编程问答-------------------- 综上所述,用lock 控制多线程操作 队列,然后单独开一个线程写数据库操作  --------------------编程问答-------------------- 为什么要一个数据库用多个线程来操作呢? --------------------编程问答-------------------- 很明显,发数过大了,导致数据库连接数量不足,要解决问题,
1,加大MySql的并发连接数量。
2,限制你代码里面的并发连接数量。 --------------------编程问答-------------------- 但是为了采集并发,必须同步写数据库啊 --------------------编程问答-------------------- 以前我做数据采集的时候,也是用多线程一起插入,发现很容易出现死锁;
然后我就用队列,多线程采集的时候只管向队列里写数据,然后另外开一条线程专门用来从队列取数据向数据库插入数据 --------------------编程问答-------------------- 10楼和24楼 已经把解决方案说了
将所有 线程的 插入数据库操作 全部委托到一个地方(我们是UI线程),然后插入队列,上锁(防止同时插入队列), 再就是楼上所说 再开一个线程专门用来处理数据, 但是注意要用委托,因为你开的这个插入数据的线程 是独立的,而数据表属于UI线程的, 我们是在这个线程里面用INVOKE方法 将操作同步到 UI线程里面。
   还有就是我感觉 你的这个数据表是UI线程的,就要使用委托和INVOKE方法。
   如果只是单独的 插入数据库,就没必要这样了,你可以将每个线程 看做一个类,每个类都包含一个插入数据操作,也是插入同一张表格(数据库表格,不是UI里面的显示表格控件), 这种情况 是不会死锁的,至少200台设备同时插入数据 我试过,没问题。 --------------------编程问答-------------------- 楼上的都没有看清楚,楼主说的是很多个进程,不是线程喔,进程之间的访问的话,如果数据库本身不支持多线程操作的话,那么就只好应用程序之间通信约定了,这是很复杂的。 --------------------编程问答--------------------
引用 25 楼  的回复:
10楼和24楼 已经把解决方案说了
将所有 线程的 插入数据库操作 全部委托到一个地方(我们是UI线程),然后插入队列,上锁(防止同时插入队列), 再就是楼上所说 再开一个线程专门用来处理数据, 但是注意要用委托,因为你开的这个插入数据的线程 是独立的,而数据表属于UI线程的, 我们是在这个线程里面用INVOKE方法 将操作同步到 UI线程里面。
  还有就是我感觉 你的这个数据表是UI线程的……

我说的是线程,请教大侠为何我插入时明显丢失数据,我只开20个线程(因为有20个连接终端),但是不同的是我的终端插入有如下循环
for(int j = 0; j < _rtnStr.Length; j+2)
                    {
                        sqlCmd = "insert into tagvalue(tagdatetime, tagcode, tagval) values(?tagdatetime, ?tagcode, ?tagval)";
                        
                        //tagdatetime = DateTime.Now;
                        //tagcode = 实验室号;
                        //tagval = _rtnStr.Substring(j, 2);
                    }


是不是因为循环插入造成独占表,使得其他线程插入数据丢失 --------------------编程问答--------------------
引用 25 楼  的回复:
10楼和24楼 已经把解决方案说了
将所有 线程的 插入数据库操作 全部委托到一个地方(我们是UI线程),然后插入队列,上锁(防止同时插入队列), 再就是楼上所说 再开一个线程专门用来处理数据, 但是注意要用委托,因为你开的这个插入数据的线程 是独立的,而数据表属于UI线程的, 我们是在这个线程里面用INVOKE方法 将操作同步到 UI线程里面。
  还有就是我感觉 你的这个数据表是UI线程的……

我说的是线程,但是如大侠所说我开20线程同时插入表确实造成数据丢失了(有20个设备),为啥啊,是不是我在线程中循环插入数据造成线程独占表?使其他线程无法插入,数据丢失?
for(int j = 0; j < _rtnStr.Length; j+2)
                    {
                        sqlCmd = "insert into tagvalue(tagdatetime, tagcode, tagval) values(?tagdatetime, ?tagcode, ?tagval)";
                        
                        //tagdatetime = DateTime.Now;
                        //tagcode = 实验室号;
                        //tagval = _rtnStr.Substring(j, 2);
                    }

--------------------编程问答-------------------- 批量加添  20个设备每产生一条数据 则   添加List<T> 中 



object locObj = new object();


List<T> data = new List<T>();


获取数据方法中

lock(locObj)
{
    data.Add(数据);


    if(data.Count>= 20)
     {
        //调用保存数据方法

          InsertData(data);

         data.Cler();
     }

}


//使用批量添加数据 SqlBulkCopy 类
void InsertData(List<T> list)
{
   
}





--------------------编程问答-------------------- 应该不是数据库的问题,毕竟一个数据库卖那么贵
你在插入数据库操作 那里加个 输出函数调试 试一下,看下每个设备是不是 都执行了 插入操作,然后再来讨论下。

--------------------编程问答-------------------- 加个 输出调试 在 插入数据库的 地方,看看 是不是每个 设备都 执行了 插入数据库操作,然后就是你说死锁,应该会抛出异常,把异常贴出来看看。
  应该不会是数据库的问题,想想一个数据库多贵啊 --------------------编程问答--------------------
引用 28 楼  的回复:
引用 25 楼  的回复:
10楼和24楼 已经把解决方案说了
将所有 线程的 插入数据库操作 全部委托到一个地方(我们是UI线程),然后插入队列,上锁(防止同时插入队列), 再就是楼上所说 再开一个线程专门用来处理数据, 但是注意要用委托,因为你开的这个插入数据的线程 是独立的,而数据表属于UI线程的, 我们是在这个线程里面用INVOKE方法 将操作同步到 UI线程里面。
还有就是我感觉……

既然你插入的数据列一样,为什么不先存进入一个table中
然后用SqlBulkCopy入库呢 --------------------编程问答--------------------
引用 32 楼  的回复:
引用 28 楼 的回复:

引用 25 楼 的回复:
10楼和24楼 已经把解决方案说了
将所有 线程的 插入数据库操作 全部委托到一个地方(我们是UI线程),然后插入队列,上锁(防止同时插入队列), 再就是楼上所说 再开一个线程专门用来处理数据, 但是注意要用委托,因为你开的这个插入数据的线程 是独立的,而数据表属于UI线程的, 我们是在这个线程里面用INVOKE方法 将操作同步到 UI……

SqlBulkCopy入库是什么技术啊 --------------------编程问答-------------------- 批量导入 --------------------编程问答-------------------- 用不了那么复杂吧?
在类里面声明一个全局对象

private static readonly object locker = new object();
......
......
然后在你的代码
lock(locker) 

   rtn = cmd.ExecuteNonQuery();
}
--------------------编程问答-------------------- 设计一下代码逻辑,尽量不要使用嵌套锁 --------------------编程问答-------------------- 不要把与数据库有关的操作 放在 通讯线程中   单开一个线程统一操作数据库 --------------------编程问答-------------------- 貌似跟死不死锁关系不大   是设计上的问题 --------------------编程问答-------------------- 高手.... --------------------编程问答-------------------- --------------------编程问答-------------------- 遇到相同问题,求解 --------------------编程问答-------------------- 频繁操作数据库,使用长链接。
不停建立链接,压力大。
数据库操作建立队列排队等待。 --------------------编程问答-------------------- 10楼和24楼 已经把解决方案说了
将所有 线程的 插入数据库操作 全部委托到一个地方(我们是UI线程),然后插入队列,上锁(防止同时插入队列), 再就是楼上所说 再开一个线程专门用来处理数据, 但是注意要用委托,因为你开的这个插入数据的线程 是独立的,而数据表属于UI线程的, 我们是在这个线程里面用INVOKE方法 将操作同步到 UI线程里面。
   还有就是我感觉 你的这个数据表是UI线程的,就要使用委托和INVOKE方法。
   如果只是单独的 插入数据库,就没必要这样了,你可以将每个线程 看做一个类,每个类都包含一个插入数据操作,也是插入同一张表格(数据库表格,不是UI里面的显示表格控件), 这种情况 是不会死锁的,至少200台设备同时插入数据 我试过,没问题。  --------------------编程问答-------------------- 死锁就是两个或多个线程抢一个对象,最后导致多线程卡住,用lock防止 --------------------编程问答-------------------- 数据库连接的死锁最好的解决办法,就是不同线程用不同的数据库连接对象去执行sql.
补充:.NET技术 ,  C#
CopyRight © 2012 站长网 编程知识问答 www.zzzyk.com All Rights Reserved
部份技术文章来自网络,