Обратный вызов для задачи, почему блокирует поток пользовательского интерфейса - PullRequest
1 голос
/ 27 мая 2019

У меня есть следующий фрагмент кода:

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)

И в моем коде окна написано следующее:

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

Так что при вызове этого с помощью метода Wait за задачей останавливается выполнение при обратном вызове ContinueWith @ the OpenSqlConnectionAsync метод.Окно зависает.Кажется, он не заканчивается, и похоже, что поток пользовательского интерфейса блокируется, что имеет смысл из моих элементарных представлений о поведении потоков.Ему вообще не нужно блокировать, но этот метод должен быть выполнен, прежде чем что-либо еще будет работать, поэтому не будет иметь никакого значения, если пользовательский ввод заблокирован, пока установлено соединение.

Теперь меня интересует, почему, если я удаляю инструкцию Wait (), await в обратном вызове, кажется, выполняется без ошибок, потому что это пустая инструкция, которая не может потерпеть неудачу., а затем информация для пользователя отображается в пользовательском интерфейсе.

1 Ответ

1 голос
/ 27 мая 2019

Этот код:

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

... в частности, Wait() блокирует выполнение потока пользовательского интерфейса. Между прочим, вызов метода async и его явная блокировка путем вызова Wait, кстати, отрицательно сказывается на цели async/await. Это как раскручивать нить только до Join. Есть моменты, когда это нормально, например, когда подпись метода не может быть изменена на async (как в консольных приложениях Main методы до C # 7)

Между тем, следующий код пытается синхронизировать поток из любого текущего потока в поток пользовательского интерфейса, чтобы поток пользовательского интерфейса обновил свойство lblCursorPosition.Text.

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

К сожалению, как мы уже упоминали, поток пользовательского интерфейса уже занят , ожидая завершения OpenSqlConnectionAsync. Так что теперь у вас есть случай, когда оба конца ждут другого. У вас тупик .

Исправление будет состоять в том, чтобы изменить сигнатуру метода следующим образом:

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

Вы можете исправить это по-другому, изменив Invoke на BeginInvoke. Последний отправляет действие асинхронно в поток пользовательского интерфейса. Чистый эффект будет OpenSqlConnectionAsync вернет GetSqlConnection; поток пользовательского интерфейса возобновит работу после Wait(); и позже обработайте обновление Label.

MainWindow.Instance.lblCursorPosition.Dispatcher.BeginInvoke(() => 
      { MainWindow.Instance.lblCursorPosition.Text = "Connection opened!"; });
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...