【问题标题】:"deadlocked on lock | communication buffer resources" exception in C# / SQLSERVERC#/SQLSERVER 中的“死锁锁定 | 通信缓冲区资源”异常
【发布时间】:2017-01-02 16:35:56
【问题描述】:

我有一个 c# 程序与运行在不同客户端 PC 上的 SQL Server 数据库一起工作。 现在,我收到了这个异常,它 [如果没有被捕获] 会关闭我的应用程序:

事务(进程 ID ...)因锁定而死锁 |沟通 与另一个进程缓冲资源,并已被选为 僵局受害者。重新运行事务。

堆栈跟踪显示异常发生在这里:

在 Gui.DB.sqlServerWrapper.MarkAsNonFreshSample(字符串条形码,Int32 devID)

这里提到的方法是这样的:

        public void MarkAsNonFreshSample(string barcode, int devID)
        {
            mux.WaitOne();
            var sql = "DELETE FROM results WHERE barcode=@barcode AND devID=@devID";
            var command = new SqlCommand(sql, conn);
            command.Parameters.AddWithValue("barcode", barcode);
            command.Parameters.AddWithValue("devID", devID);
            command.ExecuteNonQuery();
            mux.ReleaseMutex();
        }

我正在使用“mux”互斥锁来独占访问 sql 连接“conn”,因为我的程序有多个线程。

我没有使用复杂查询或长事务(我猜这个短查询是一个不会导致死锁的事务。我错了吗?)。

问题出在哪里,我应该如何解决?

【问题讨论】:

  • 删除互斥锁和全局连接。全局连接保证死锁并且在出错的情况下根本不可能恢复。连接应尽可能短地保持打开状态
  • 您可以检索the deadlock graph 并将其包含在您的问题中吗?
  • 虽然共享连接本身不一定是错误的实际原因,但早期使用连接可能已经启动了事务并执行了活动而没有提交或回滚。如果多个客户端执行此操作,然后稍后尝试删除行,则不同的客户端可能因此彼此死锁。请向我们展示使用此共享连接的所有代码——仅此代码本身不太可能遇到此类死锁错误。

标签: c# sql-server deadlock


【解决方案1】:

共享这样的连接不是一个好习惯

public void MarkAsNonFreshSample(string barcode, int devID)
{
    using (SqlConnection con = new SqlConnection(conString))
    {
        con.Open();
        using (SqlCommand command = con.CreateCommand())
        {
            command.CommandText = "DELETE FROM results WHERE barcode=@barcode AND devID=@devID";
            command.Parameters.AddWithValue("barcode", barcode);
            command.Parameters.AddWithValue("devID", devID);
            command.ExecuteNonQuery();
        }
    } 
}

【讨论】:

  • @Akhir 使用全局长时间运行的连接互斥锁无论如何都会导致大问题。只是长时间运行的连接就足以积累锁并导致死锁。您通过该连接读取的任何内容都会获得选择锁,这意味着当其他人第一次尝试进行更新时,您将遇到死锁。
  • @Akhir 其次,您所做的是花费 1000 倍以上的费用,因为长连接会导致阻塞和死锁。 ADO.NET 使用连接池,因此 不需要 每次都创建新的服务器连接。关闭的连接被重置并放回池中,准备重用。至少从 1995 年开始,ADO 之前也这样做过。性能和可扩展性的差异是数量级,有利于池化,因为 5-6 个连接最终可以服务数百个请求。
  • 最后,在单独的线程中调用像ExecuteNonQuery 这样的阻塞方法是假的多线程。您只需阻塞等待数据库响应的线程。您应该使用像ExecuteNonQueryAsync 这样的异步方法,它 阻塞任何线程。使用操作系统的 IO 完成端口功能来等待响应而不使用线程。只需几个 ThreadPool 线程即可处理数百个请求
  • 也使用await conn.OpenAsync()。它也有很大的不同 - 请参阅:stackoverflow.com/questions/41399766/…
  • 这不是问题的答案;因为持久连接与问题无关。
猜你喜欢
  • 1970-01-01
  • 2011-10-09
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2018-12-23
  • 1970-01-01
相关资源
最近更新 更多