尝试使用异步/等待和事务将多条记录插入 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/