使用async/await时UI仍然冻结

3

我是一名在异步操作方面不太专业的人,希望有人能帮我指出问题。

在我的其中一个方法中,我编写了一个包装器,

public static async Task<int> ExecuteNonQueryRawSQLAsync(string sqlStatement)
{
    int _returnValue = -1;
    using (SqlConnection _conn = new SqlConnection("connectionString"))
    {
        using (SqlCommand _comm = new SqlCommand())
        {
            _comm.Connection = _conn;
            _comm.CommandText = sqlStatement;
            _comm.CommandType = CommandType.Text;

            // other codes on setting parameter

            try
            {
                await _conn.OpenAsync();
                _returnValue = Convert.ToInt32(await _comm.ExecuteNonQueryAsync());
            }
            catch (Exception)
            {
                throw;
            }
        }
    }
    return _returnValue;
}

在我的用户界面中,我这样调用方法:

int _recordsAffected = await MyClass.ExecuteNonQueryRawSQLAsync("INSERT....");

为了测试它是否真正起作用,我尝试在连接字符串中提供一个无效的数据库服务器地址,以便它不断搜索直到抛出异常。

当程序仍在连接时,UI会冻结。我的包装器缺少哪些东西?还是我需要做其他必要的初始化?


你调用 ExecuteNonQueryRawSQLAsync 方法的方式 - 是异步的吗? - Chris Mantle
@ChrisMantle 这是我的包装器的名称。在包装器内部,有一个异步方法 ExecuteNonQueryAsync() - Rossana
2个回答

5

从痛苦的记忆中可以得知,TCP套接字有一个非常类似的问题——基本上,名称解析是同步执行的,即使是异步操作。有两种方法可以解决这个问题:

  • 使用IP地址而不是名称
  • 确保你从工作线程启动Open* / Connect*或者其他的操作,例如Task.StartNew

未经测试,但可以推断:

await Task.StartNew(_conn.Open);

1
@SriramSakthivel 我还没有看过;基本上,我是通过遇到一个非常类似的问题而艰难地发现了这个问题 - 如果我没记错的话,那时我瞥了一眼反编译器。 - Marc Gravell
虽然我鼓励使用TaskRun而不是Task.Factory.StartNew(在我的博客上有详细解释),但这里表示赞同+1。 - Stephen Cleary
2
@Rossana:我的上一条评论中有一个链接,链接到我的博客,在那里我解释了为什么我不推荐使用StartNew。我建议您按照Marc的建议更改您的包装器,但使用Run而不是Factory.StartNew - Stephen Cleary
1
@Rossana:是的,那就是我的意思。 - Stephen Cleary
2
@Rossana:这是Marc在他的答案中描述的相同问题。总结一下,许多.NET网络调用实际上并不是异步的。 - Stephen Cleary
显示剩余9条评论

0

使用 await "somemethod" 并不意味着该方法被异步调用。您仍然在进行常规方法调用,直到被调用的方法决定返回表示异步操作的任务,然后您才会等待。

如果该方法在返回可等待任务之前执行了大量耗时的操作,则可以使用 TaskFactory.Startnew() 进行调用。


那么我的回答和这个有什么不同呢?https://dev59.com/KWMk5IYBdhLWcg3wtgMR#19029317 - Rossana
@Rossana 注意帖子中提到“正确编写方法”。如果在创建任务时方法被阻塞,那么它就不是“正确编写的”。异步方法应该立即返回。如果创建任务很耗时(例如需要解析DNS命名等),创建过程本身应该封装在一个新任务中,以便客户端将整个过程视为一个可等待的任务。 - Tormod

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接