c# - 使用事务的批量插入会阻止异步进程上的 UI

标签 c# mysql asynchronous transactions async-await

尝试使用异步/等待和事务将多条记录插入 MySQL 数据库,但它仍然导致 UI 在循环中变得卡住/无响应。

看看下面的代码,我做错了什么或者如何实现,以便 UI 在此过程中仍然响应。

异步方法

public static async Task AddRecords() {
    foreach ( string month in Months ) {
        await MakeTable( month );
        string query="INSERT INTO `"+month+"` ( Caller, Started, Dialed, DurationSec, DurationMin, Cost, Location, Switch ) VALUES (@Caller, @Started, @Dialed, @DurationSec, @DurationMin, @Cost, @Location, @Switch);";
        using ( MySqlConnection cn=new MySqlConnection( ConnectionString.ToString() ) ) {
            await cn.OpenAsync();
            using ( MySqlTransaction trans=cn.BeginTransaction() ) {
                using ( MySqlCommand cmd=new MySqlCommand( query, cn, trans ) ) {
                    cmd.CommandType=CommandType.Text;
                    foreach ( Record r in CDR.Records ) {
                        if ( r.Started.ToString( "yyyy-MM" )==month ) {
                            cmd.Parameters.Clear();
                            cmd.Parameters.AddWithValue( "@Caller", r.Caller );
                            cmd.Parameters.AddWithValue( "@Started", r.Started );
                            cmd.Parameters.AddWithValue( "@Dialed", r.Dialed );
                            cmd.Parameters.AddWithValue( "@DurationSec", r.Duration );
                            cmd.Parameters.AddWithValue( "@DurationMin", Math.Ceiling( r.Duration/60 ) );
                            cmd.Parameters.AddWithValue( "@Cost", r.Cost );
                            cmd.Parameters.AddWithValue( "@Location", r.Location );
                            cmd.Parameters.AddWithValue( "@Switch", r.Switch.ToString() );
                            cmd.ExecuteNonQuery();
                        }
                    }
                    trans.Commit();
                }
            }
            await cn.CloseAsync();
        }
    }
}

有关如何调用它的片段:

    private async void button1_Click( object sender, EventArgs e ) {
         this.Text = "Adding Records";
         await AddRecords();
         this.Text = "Completed";
    }

顺便说一句,当 UI 阻塞时,它不应该在执行完所有先前代码后阻塞。例如,在上面的按钮点击方法中,第一个 'this.Text' 没有设置,因为一旦 await AddRecords(); 执行,它就发生在 UI 有机会完成更新之前,并且直到阻塞完成后才会完成,这导致只有 this.Text - "Completed" 在 UI 级别被注意到。


更新

cmd.ExecuteNonQuery(); 修改为 await cmd.ExecuteNonQueryAsync();(Yuval Itzchakov 推荐)后,UI 仍然阻塞,这让我相信了在 trans.Commit(); 行或与交易构建方式有关的事情上阻塞。

更新代码

public static async Task AddRecords() {
    foreach ( string month in Months ) {
        await MakeTable( month );
        string query="INSERT INTO `"+month+"` ( Caller, Started, Dialed, DurationSec, DurationMin, Cost, Location, Switch ) VALUES (@Caller, @Started, @Dialed, @DurationSec, @DurationMin, @Cost, @Location, @Switch);";
        using ( MySqlConnection cn=new MySqlConnection( ConnectionString.ToString() ) ) {
            await cn.OpenAsync();
            using ( MySqlTransaction trans=cn.BeginTransaction() ) {
                using ( MySqlCommand cmd=new MySqlCommand( query, cn, trans ) ) {
                    cmd.CommandType=CommandType.Text;
                    foreach ( Record r in CDR.Records ) {
                        if ( r.Started.ToString( "yyyy-MM" )==month ) {
                            cmd.Parameters.Clear();
                            cmd.Parameters.AddWithValue( "@Caller", r.Caller );
                            cmd.Parameters.AddWithValue( "@Started", r.Started );
                            cmd.Parameters.AddWithValue( "@Dialed", r.Dialed );
                            cmd.Parameters.AddWithValue( "@DurationSec", r.Duration );
                            cmd.Parameters.AddWithValue( "@DurationMin", Math.Ceiling( r.Duration/60 ) );
                            cmd.Parameters.AddWithValue( "@Cost", r.Cost );
                            cmd.Parameters.AddWithValue( "@Location", r.Location );
                            cmd.Parameters.AddWithValue( "@Switch", r.Switch.ToString() );
                            await cmd.ExecuteNonQueryAsync();
                        }
                    }
                    trans.Commit();
                }
            }
            await cn.CloseAsync();
        }
    }
}

最佳答案

使用 ExecuteNonQueryAsync 而不是 ExecuteNonQuery:

await cmd.ExecuteNonQueryAsync();

这是查询中最耗时的调用,因为您使用的是同步版本,所以它被同步阻塞。

编辑:

因为你不需要做任何需要同步上下文的 UI 工作,你可以使用 ConfigureAwait(false),你可以将它应用到 OpenAsync,然后延续将在线程池 IO worker 上运行。

关于c# - 使用事务的批量插入会阻止异步进程上的 UI,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/31336162/

相关文章:

C#:我应该如何转换以下内容?

C#:如何将 const 覆盖从 C++ 转换为 C#

mysql:如果时间戳超过 10 小时,则更新时间戳

mysql - 慢查询挂起服务器

c# - 使用 WCF REST 服务进行基本身份验证的内置方法?

mysql - 更改表默认字符集修改MySQL 5.6中的行

c++ - 提升 asio 异步 udp 服务器 - 性能不佳

asp.net-mvc - 什么时候应该在 asp.net mvc 2 中使用异步 Controller ?

c# - 使用 Begin* End* 方法时,在 C# 中处理数据包碎片的工作示例是什么样的?

c# - 程序卡住——没有错误,没有异常