我有以下代码:
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 似乎可以完美执行而不会卡住(因为这是一个空指令,不能失败),然后在用户界面中显示用户信息。
答案 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!"; });