【问题标题】:Deadlocks caused due to async await [MySQL Database]由于异步等待导致的死锁 [MySQL 数据库]
【发布时间】:2018-02-01 19:48:58
【问题描述】:

我有以下异步方法,可以删除记录并将记录插入 MySQL 数据库

public async Task<Res> myNewFunc(int id, IEnumerable<Sample> obs)
{      
    using (var t = DbConnection.GetTransaction())
    {
        await DbConnection.DeleteMany<M1>().Where(x.Id == id).ExecuteAsync();
        foreach (var ob in obs)
        {
            await DbConnection.InsertAsync(ob);
        }
        t.Complete();
    }
}

调用 myNewFunc() 的方法:

public async Task<T> method()
{
    //...
    //...
    var response = await DAL.myNewFunc(id, obs);
}

使用 foreach 循环访问异步方法。

foreach(var num in nums)
{
    method();
}

我在使用上面的代码时遇到了 MySQL 的死锁问题。

【问题讨论】:

  • foreach 看起来很奇怪;您正在迭代 nums 但不使用该值。您正在调用 method() 而不等待它或存储任务以供稍后等待。
  • 只有锁的范围在中途扩展时才会出现死锁。积极锁定您可能需要的任何资源很重要,而不仅仅是您确定会需要的资源。如果没有看到 SQL,真的很难判断 DB 端会发生什么。
  • 同意@Christopher,您应该查看数据库并找到导致死锁的实际原因。我不熟悉解决 MySql 中的死锁问题,但快速搜索表明您可以找到导致问题的实际语句。
  • 由于 foreach 没有等待,它将在不停止/暂停/等待的情况下运行所有​​迭代。这将导致 X 个“方法”调用被连续触发。每个人都会立即调用 myNewFunc 并等待。然后这会调用您的 DeleteMany 并等待。但是对 myNewFunc 和 DeleteMany 的调用并不知道对方,如果他们试图删除同一张表中的东西,你会遇到死锁,正如你所看到的。这将与该删除中调用的触发器和其他表一起传播。
  • t.commit() 而不是完整的呢?

标签: c# mysql multithreading entity-framework async-await


【解决方案1】:

因为您没有在最后一个 for 循环中等待,所以它将为所有这些循环启动线程并同时并行运行所有这些线程。如果你这里有很多数据,他们都将争夺相同的资源。您最终会在每种情况下创建事务,然后从表中删除多条记录。然而,这可能不是真正的根本问题。在您的所有 ExecuteAsync 调用中,您不执行 .ConfigureAwait(false)。这意味着在运行每个语句后,它会一直等待,直到它可以在原始线程上恢复工作,在这种情况下是 UI 线程。我不知道你在 for 循环之后做了什么,但它可能这个线程永远不会放手,所以这些 db 调用都不会回来,你会遇到死锁。

您可以尝试两种方法来解决此问题:

  1. 每个 Async() 调用都必须是 Async().ConfigureAwait(false)。如果确实是 UI 线程被锁定,这可能会修复它。

  2. 在最后一个 for 循环中,您需要等待每个循环,因为数据库资源可能都被占用了。请改用此代码:

 

foreach(var num in nums)
{
    method().GetAwaiter().GetResult();
}

这一次只会处理一个。对于 #2,您可能还需要执行 #1。

【讨论】:

    猜你喜欢
    • 2021-09-18
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2016-12-22
    相关资源
    最近更新 更多