多次插入和更新导致死锁 c#

Posted

技术标签:

【中文标题】多次插入和更新导致死锁 c#【英文标题】:Multiple Insert and Update causing DeadLock c# 【发布时间】:2012-12-26 07:37:39 【问题描述】:

我的应用程序有两个部分,它们分别执行大量插入和更新,并且由于管理不善或管理不善,出现了死锁。

我正在使用实体框架进行插入和更新。

以下是我的 TestSpool 程序的代码。该程序的目的是在给定的时间间隔内插入 x 条记录。

using System;
using System.Linq;
using System.Threading;
using System.Transactions;
namespace TestSpool

    class Program
    
        static void Main(string[] args)
        
            using (var db = new TestEntities())
            
                decimal start = 700001;
                while (true)
                
                    using (TransactionScope scope = new TransactionScope())
                    
                        //Random ir = new Random();
                        //int i = ir.Next(1, 50);
                        var objs = db.BidItems.Where(m => m.BidItem_Close == false);
                        foreach (BidItem bi in objs)
                        
                            for (int j = 0; j <= 10; j++)
                            
                                Transaction t = new Transaction();
                                t.Item_Id = bi.BidItemId;
                                t.User_Id = "Ghost";
                                t.Trans_Price = start;
                                t.Trans_TimeStamp = DateTime.Now;
                                start += 10;
                                db.Transactions.AddObject(t);

                            
                            Console.WriteLine("Test Spooled for item " + bi.BidItemId.ToString() + " of " + 11 + " bids");
                            db.SaveChanges();
                        

                        scope.Complete();
                        Thread.Sleep(5000);
                    
                
            
        
    

程序的第二部分是testserverclass,该serverclass应该处理来自testspool的大量事务,并确定最大事务量并更新到另一个表。

using System;
using System.Linq;
using System.Transactions;
public class TestServerClass


    public void Start()
    
        try
        

            using (var db = new TestServer.TestEntities())
            

                while (true)
                
                    using (TransactionScope scope = new TransactionScope())
                    
                        var objsItems = db.BidItems.Where(m => m.BidItem_Close == false);
                        foreach (TestServer.BidItem bi in objsItems)
                        
                            var trans = db.Transactions.Where(m => m.Trans_Proceesed == null && m.Item_Id == bi.BidItemId).OrderBy(m => m.Trans_TimeStamp).Take(100);

                            if (trans.Count() > 0)
                            
                                var tran = trans.OrderByDescending(m => m.Trans_Price).FirstOrDefault();

                                // TestServer.BidItem bi = db.BidItems.FirstOrDefault(m => m.BidItemId == itemid);
                                if (bi != null)
                                
                                    bi.BidMinBid_LastBid_TimeStamp = tran.Trans_TimeStamp;
                                    bi.BidMinBid_LastBidAmount = tran.Trans_Price;
                                    bi.BidMinBid_LastBidBy = tran.User_Id;

                                
                                foreach (var t in trans)
                                
                                    t.Trans_Proceesed = "1";
                                    db.Transactions.ApplyCurrentValues(t);
                                

                                db.BidItems.ApplyCurrentValues(bi);
                                Console.WriteLine("Processed " + trans.Count() + " bids for Item " + bi.BidItemId);
                                db.SaveChanges();

                            


                        
                        scope.Complete();

                    

                

            


        
        catch (Exception e)
        
            Start();
        
    


但是,由于两个应用程序同时运行,无论是从第一个测试应用程序还是服务器应用程序,它都会很快随机进入死锁。我如何优化双方的代码以防止死锁?我期待来自测试池应用程序的大量插入。

【问题讨论】:

首先,您能否暂时从服务器代码中删除 catch 块,看看会发生什么?到目前为止,您基本上有一个没有基本案例的服务器代码的递归方法。 另外,在您的客户端代码中,在事务范围完成后(在 using 语句结束后)设置睡眠,看看这对输出有何影响? 如果没有服务器的try catch,应用程序都会遇到错误并死掉。 【参考方案1】:

由于它们处理相同的数据并相互影响,我相信最干净的方法是避免同时执行这两个。

定义一个全局静态变量,或互斥体或某种标志,可能在数据库上。开始执行的人举起旗帜,其他人等待旗帜下降。当标志下降时,另一个升起标志并开始执行。

为避免每个班级的等待时间过长,您可以更改两个班级,使其每回合只处理有限数量的记录。您还应该为标志引入最大等待时间。应谨慎选择最大记录限制,以确保每个类在比最大等待时间更短的时间内完成其工作。

【讨论】:

测试池是在给定的时间范围内模拟来自网络界面的多个随机插入量。因此,谁先走是不可预知的。应该是谁先出价。 TestSpool 模拟来自 Web 界面的随机插入,TestServerClass 模拟服务器级别的批量处理记录。我对吗?因此,您应该在开始在服务器上进行批量处理之前升旗。投标插入将在一个循环中等待标志,并在标志下降时继续。这将避免死锁。甚至在您开始交易之前就应该进行等待,并且您应该在将投标记录置于等待状态之前为其添加时间戳,这样您就不会丢失准确的时间信息。 嗨,如果我使用 flag 方法,您认为将所有出价都插入到静态类中的队列中是否可行.. 并且使用 while 循环,如果 flag 上升,则服务器将处理出价? 假设用户出价的确切时间对您的业务案例很重要,您应该在将出价放入任何类型的队列之前为其添加时间戳。之后,您可以将出价放入静态队列或将它们放入单独的数据库表中以供以后处理,或者您可以简单地让所有出价在其线程中的“while循环”中等待,直到标志关闭。如果您选择静态队列或临时表方法,您可以控制订单出价是标志关闭后的流程,如果您选择“while 循环”方法,出价可能会在标志关闭后以任何顺序出现,但它会...... ... 可能无关紧要,因为您在进入等待循环之前为它们加上时间戳。任何一种情况标志都必须使用互斥体、信号量或某种锁小心地实现,以确保线程安全。

以上是关于多次插入和更新导致死锁 c#的主要内容,如果未能解决你的问题,请参考以下文章

更新提交以避免死锁

由于多线程插入导致 MySQL 死锁

为啥并发的“删除...插入”语句会导致死锁?

避免 Task.Run 导致主线程死锁(C#)

从 F# 调用 C# 异步方法会导致死锁

C# 死锁的原理与排查方法详解