所以,我在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不同的线程中。所以,我想现在纠正两个例外......
答案 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>
+-------------------+