Warning: file_get_contents(/data/phpspider/zhask/data//catemap/2/.net/21.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
C# GUI线程上的IDbConnection.BeginTransaction,使用async/await,导致多个调用死锁_C#_.net_Multithreading_Transactions_Async Await - Fatal编程技术网

C# GUI线程上的IDbConnection.BeginTransaction,使用async/await,导致多个调用死锁

C# GUI线程上的IDbConnection.BeginTransaction,使用async/await,导致多个调用死锁,c#,.net,multithreading,transactions,async-await,C#,.net,Multithreading,Transactions,Async Await,我有一个问题,我完全理解,但正在努力解决 我有一个使用async/wait的事件循环 我在所有数据库操作上使用async/await,所有continuations都将运行事件循环的同一个线程登录 我还需要使用事务,在它的生命周期中,它将有一些其他的延续 这样说,考虑这个代码。 public异步任务UpdateItem(int-mediateItemId) { 使用(var connection=\u dataService.OpenDbConnection()) { 使用(var transa

我有一个问题,我完全理解,但正在努力解决

  • 我有一个使用
    async
    /
    wait
    的事件循环
  • 我在所有数据库操作上使用
    async
    /
    await
    ,所有continuations都将运行事件循环的同一个线程登录
  • 我还需要使用事务,在它的生命周期中,它将有一些其他的延续
  • 这样说,考虑这个代码。

    public异步任务UpdateItem(int-mediateItemId)
    {
    使用(var connection=\u dataService.OpenDbConnection())
    {
    使用(var transaction=connection.BeginTransaction())
    {
    var item=wait connection.SingleByIdAsync(mediaItemId);
    item.Index++;
    等待连接。更新同步(项目);
    Commit();
    }
    }
    }
    
    此方法的调用方来自主事件循环

    我首先创建一个事务。使用SqlLite,这实际上是一个数据库级互斥。这意味着,当事务正在进行时,对
    BeginTransaction
    的其他调用将被阻止

    现在,考虑这个方法被多次调用,快速连续。在

    wait
    ing
    SingleByIdAsync
    之后,第二个调用将尝试
    BeginTransaction
    ,但它将在那里等待,直到第一个事务完成。这是意料之中的,只是它会阻止主事件循环,阻止任何进一步的继续发生,使第一个事务保持打开状态

    轰,死锁

    如果存在一个
    IDbConnection.BeginTransactionAsync
    ,则可以解决这个问题,而实际上没有。这样我就可以跳出事件循环,并在事务成功打开后继续

    ,请考虑下面的修复:

    public异步任务UpdateItem(int-mediateItemId)
    {
    使用(var connection=\u dataService.OpenDbConnection())
    {
    //请注意,我们正在等待交易的开始。
    使用(var transaction=wait Task.Run(()=>connection.BeginTransaction())
    {
    var item=wait connection.SingleByIdAsync(mediaItemId);
    item.Index++;
    等待连接。更新同步(项目);
    Commit();
    }
    }
    }
    
    话虽如此,您认为打开未打开数据库连接的线程的事务有什么害处吗?为什么没有
    IDbConnection.BeginTransactionAsync
    wait Task.Run(()=>connection.BeginTransaction())
    是可接受的解决方案吗

    这样说,您是否看到打开未打开数据库连接的线程事务有什么危害

    这里有几个考虑因素。第一个是在与打开db连接的线程不同的线程上打开事务。另一种情况是,您在与打开事务的线程不同的线程上提交和处理事务

    wait Task.Run(()=>connection.BeginTransaction())是可接受的解决方案吗

    “这是个问题吗?”问题只能由(客户机)数据库提供商回答

    如果要确保不会出现问题,可以包括您自己的互斥:

    private static readonly SemaphoreSlim _mutex = new SemaphoreSlim(1);
    public async Task UpdateItem(int mediaItemId)
    {
      using (var connection = _dataService.OpenDbConnection())
      {
        await _mutex.WaitAsync();
        try
        {
          using (var transaction = await connection.BeginTransaction())
          {
            var item = await connection.SingleByIdAsync<MediaItem>(mediaItemId);
            item.Index++;
            await connection.UpdateAsync(item);
    
            transaction.Commit();
          }
        }
        finally
        {
          _mutex.Release();
        }
      }
    }
    
    private static readonly SemaphoreSlim\u mutex=new SemaphoreSlim(1);
    公共异步任务更新项(int mediaItemId)
    {
    使用(var connection=\u dataService.OpenDbConnection())
    {
    wait_mutex.WaitAsync();
    尝试
    {
    使用(var transaction=await connection.BeginTransaction())
    {
    var item=wait connection.SingleByIdAsync(mediaItemId);
    item.Index++;
    等待连接。更新同步(项目);
    Commit();
    }
    }
    最后
    {
    _mutex.Release();
    }
    }
    }
    

    private static readonly SemaphoreSlim\u mutex=new SemaphoreSlim(1);
    公共异步任务更新项(int mediaItemId)
    {
    使用(var connection=\u dataService.OpenDbConnection())
    使用(_mutex.LockAsync())
    使用(var transaction=await connection.BeginTransaction())
    {
    var item=wait connection.SingleByIdAsync(mediaItemId);
    item.Index++;
    等待连接。更新同步(项目);
    Commit();
    }
    }
    
    能否显示“事件循环”。NET通常没有这样的东西,至少在这里不是这样建议的。它是基于油嘴滑舌的。这很难描述,但我正在使用qmlnet。请找到一种方式来传达情况的本质。是否有一个线程具有同步上下文?是的,有一个线程具有事件循环,具有自定义同步上下文,可将继续发送到单个线程上运行的事件循环。谢谢!我刚刚研究了.NET sqlite实现,特别是
    sqlite3\u exec
    ,它是线程安全的。我将在另一条线上等待。
    private static readonly SemaphoreSlim _mutex = new SemaphoreSlim(1);
    public async Task UpdateItem(int mediaItemId)
    {
      using (var connection = _dataService.OpenDbConnection())
      using (_mutex.LockAsync())
      using (var transaction = await connection.BeginTransaction())
      {
        var item = await connection.SingleByIdAsync<MediaItem>(mediaItemId);
        item.Index++;
        await connection.UpdateAsync(item);
    
        transaction.Commit();
      }
    }