回调任务,为什么UI线程会阻塞

时间:2019-05-27 10:46:05

标签: c# asynchronous callback

我有以下代码:

public static async Task<SqlConnection> OpenSqlConnectionAsync()
{
    if (_SqlConnection == default(SqlConnection))
    {
        _SqlConnection = new SqlConnection();
    }
    if (_SqlConnection.State == ConnectionState.Closed || _SqlConnection.State == ConnectionState.Broken)
    {
        _SqlConnection.ConnectionString = SqlConnectionStuff.GetConnectionString;
        Task ConnectionTask = _SqlConnection.OpenAsync();
        await ConnectionTask.ContinueWith((PreviousTask) =>
       {

       }
        );
        if (_SqlConnection.State == ConnectionState.Open)
        {
            MainWindow.Instance.lblCursorPosition.Dispatcher.Invoke(() => { MainWindow.Instance.lblCursorPosition.Text = "Connection opened!"; });
        }
        else
        {
            MainWindow.Instance.lblCursorPosition.Dispatcher.Invoke(() => { MainWindow.Instance.lblCursorPosition.Text = "Connection not opened!"; });
        }
    }
    return GetSqlConnection;
}

在一个单独的类中,它的描述名称为SqlConnectionStuff ...(不用担心,它将很快被更改; P)

在我的Window代码中,下面写着:

private void Window_Loaded(object sender, RoutedEventArgs e)
{
    SqlConnectionStuff.OpenSqlConnectionAsync().Wait();
}

因此,当使用Task后面的Wait方法调用此函数时,请在 OpenSqlConnectionAsync 方法的 ContinueWith 回调处停止执行。 窗户冻结了。它似乎还没有完成,并且好像UI线程被阻塞了,这从我对线程行为的初步了解中可以理解。完全不需要阻塞,但是必须先执行此方法,然后其他任何方法才能起作用,因此只要建立连接就锁定用户输入,就没有关系。

我现在的兴趣是,为什么如果删除Wait()指令,回调上的 await 似乎可以完美执行而不会卡住(因为这是一个空指令,不能失败),然后在用户界面中显示用户信息。

1 个答案:

答案 0 :(得分:2)

此代码:

private void Window_Loaded(object sender, RoutedEventArgs e)
{
    SqlConnectionStuff.OpenSqlConnectionAsync().Wait();
}

...特别是Wait()阻止UI线程执行。顺便说一句,先调用async方法,然后再通过调用Wait对其进行显式阻止,这将破坏async/await的目的。这就像只在其上Join上旋转一个线程。有时可以这样做,例如无法将方法的签名更改为async(例如在C#7之前的控制台应用程序Main方法中)

同时,以下代码尝试从当前线程到UI线程的同步封送处理,以使UI线程更新lblCursorPosition.Text属性。

MainWindow.Instance.lblCursorPosition.Dispatcher.Invoke(() =>
       { MainWindow.Instance.lblCursorPosition.Text = "Connection opened!"; });

不幸的是,正如我们已经提到的,UI线程已经很忙,正在等待OpenSqlConnectionAsync完成。因此,现在您遇到了两端都在等待对方的情况。您有一个死锁

一种解决方法是像这样更改方法签名并调用await

private async void Window_Loaded(object sender, RoutedEventArgs e) // <-- note async
{
    // await synchronously
    await SqlConnectionStuff.OpenSqlConnectionAsync(); // await here. No Wait()
}

您可以通过将Invoke更改为BeginInvoke来解决它。后者将动作异步地发布到UI线程。最终结果是OpenSqlConnectionAsync将返回GetSqlConnection; UI线程将在Wait()之后恢复;然后进行Label的更新。

MainWindow.Instance.lblCursorPosition.Dispatcher.BeginInvoke(() => 
      { MainWindow.Instance.lblCursorPosition.Text = "Connection opened!"; });