Visual Basic中的异步/等待对不能正常工作吗?

时间:2016-12-25 17:58:43

标签: vb.net asynchronous async-await

所以,我在Visual Basic 2015中编写的程序的一部分中有以下布局:(以下只是伪代码,使其更容易理解)

Async Button_Click_Event_Handler {
   ... (Synchronous code)
   Await DoWork()
}

Async Sub DoWork() {
   ... (Synchronous Code)
   Dim InitDB_t = Task.Run(Sub() InitDatabase())
   ... (Both synchronous and awaited code)
   Await InitDB_t
   ... (Both synchronous and awaited code)
}

Async InitDatabase() {
   ... (Synchronous Code)
   Await SQLConnection.OpenAsync()
   ... (Synchronous Code)
}

现在,我想要完成的是以下流程:

Button_Click_Event_Handler ---> DoWork() ---> Start executing InitDatabase(),
while other things that don't depend on the Database get executed --->
After the call "Await InitDB_t" be ***100% SURE*** that the database has been initialized,
i.e. InitDatabase() task has been completed, so that things that depend on it get executed.

显然,VB似乎并不尊重我的流程,或者我根本不明白Await的工作原理,因为每次运行它时,我都会在{{{}}下面发生异常{ {1}}当我实际使用 - 假设初始化 - 数据库并在检查时,我意外地意识到Await InitDB_t没有实际完成,但仍然是&#34 ;等待"在InitDatabase()上,即使SQLConnection.OpenAsync()已经返回!!!

如何在不出现死锁的情况下更改我的代码,因为使用Await InitDB_t实际上会阻塞当前的执行线程(因此,我会 100%确定后面的任何内容在DB完全初始化之后执行,但是,oops,我会遇到死锁)并且因为我需要上下文来更新一些GUI元素?

编辑1 :好的,经过进一步调试后,我的逻辑似乎比我想象的更错误,因为出现了另一个异常:当我执行Task.Wait()时,我也做了一些GUI分配,例如InitDatabase()引发异常,因为我在与GUI不同的线程中。所以,我想现在纠正两个例外......

2 个答案:

答案 0 :(得分:1)

  

我很惊讶InitDatabase()实际上还没有完成,但仍在等待#34;在SQLConnection.OpenAsync()上,即使Await InitDB_t已经返回!!!

如果您使用Async Sub,就会发生这种情况。您只应将Async Sub用于事件处理程序。

  

当我执行InitDatabase()时,我也做了一些GUI分配,例如label_p.Content =" finished"因为我所处的线程与GUI不同,所以会引发异常。

取消对Task.Run的通话。你无论如何都不需要它,因为你的代码是异步的。

答案 1 :(得分:1)

您的主要问题是您使用新主题包装了InitDatabase()Task.Run返回的任务将在OpenAsync完成之前完成。

Dim InitDB_t = Task.Run(Sub() InitDatabase())

如果InitDatabase已正确创建异步方法(您没有显示完整上下文),则它将返回Task

 Private Async Function InitDatabase() As Task
     ' ... (Synchronous Code)
     Await SQLConnection.OpenAsync()
     ' ... (Synchronous Code)
 End Function   

Task完成执行后,Await SQLConnection.OpenAsync()将返回给OpenAsync行的来电者。

因此,在Task.Run内,此方法将在OpenAsync完成之前返回值 因为方法InitDatabase完成了执行任务InitDB_t的状态  实际上RanToCompletion尚未完成,OpenAsync也将Task.Run

因为您的所有方法都是异步的,所以您根本不需要InitDatabase,所有操作都可以在同一个线程上执行 - 这将成功更新UI控件。

首先确保Task返回InitDatabaseAsync。我建议将方法重命名为 Private Async Function InitDatabaseAsync() As Task ' ... (Synchronous Code) Await SQLConnection.OpenAsync() Me.MyLabel.Text = "Finished" ' ... (Synchronous Code) End Function ,后缀" Async"由惯例添加,但我发现它非常有用。

DoWork

然后使用Task方法采用相同的方法 - 始终使用Async方法返回Async Sub。例外是UI事件处理程序 - 使用它们可以使用Task 如果你没有返回 Private Async Function DoWorkAsync() As Task ' ... (Synchronous Code) Dim InitDB_t = InitDatabaseAsync() ' ... (Both synchronous and awaited code) Await InitDB_t ' ... (Both synchronous and awaited code) End Function ,那么你的方法将像" Fire和Forget"一样工作,但你不会注意到这些方法中抛出的任何异常。

Private Async Sub Button_Click_Event_Handler()
    ' ... (Synchronous code)
    Await DoWorkAsync()
End Sub

然后在eventhandler

 +----------------+
 | UITableViewCel |
 +-------+--------+
         ^
         |
 +-------+--------+
 |  BaseFormCell  |
 +-------+--------+
         ^
         |
+--------+---------+
| TypedFormCell<T> |
+--------+---------+
         ^
         |
+--------+----------+
| TextFieldFormCell |  : TypedFormCell<String>
+-------------------+