繁体   English   中英

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

[英]Async/await pair in visual basic not working as it should?

因此,在用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工作原理,因为每次运行它时,当我实际使用-假设已初始化Await InitDB_t时,都会在Await InitDB_t下得到异常经过检查,令我惊讶的是, InitDatabase()尚未真正完成,但是即使Await InitDB_t返回了,它仍然在SQLConnection.OpenAsync()上“等待”!

我如何才能在没有死锁的情况下更改代码,因为使用Task.Wait()实际上会阻塞当前执行线程(因此,我将百分百确定在数据库完全初始化之后,执行之后的所有操作,但是,糟糕,我会遇到死锁),并且由于我需要上下文来更新某些GUI元素?

编辑1 :好的,在进一步调试之后,我的逻辑似乎比我预期的要错误得多,因为会出现另一个异常:当我执行InitDatabase()我也进行了一些GUI分配,例如label_p.Content = "finished" ,该抛出例外,因为我与GUI不在同一线程中。 因此,我现在想更正这两种例外情况...

令我惊讶的是,InitDatabase()尚未真正完成,但是即使Await InitDB_t已经返回,它仍然在SQLConnection.OpenAsync()上“等待”!

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

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

删除对Task.Run的调用。 无论如何,您都不需要它,因为您的代码是异步的。

您的主要问题是用新线程包装了InitDatabase() Task.Run返回的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   

OpenAsync完成执行后,继续执行下一行,然后Task将在OpenAsync Await SQLConnection.OpenAsync()行上返回给调用方。

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

由于所有方法都是异步的,因此根本不需要Task.Run ,并且所有操作都可以在同一线程上执行-这将成功更新UI控件。

首先,请确保InitDatabase返回Task 我建议将方法重命名为InitDatabaseAsync ,约定中添加了后缀“ Async”,但我发现它非常有用。

 Private Async Function InitDatabaseAsync() As Task
     ' ... (Synchronous Code)
     Await SQLConnection.OpenAsync()
     Me.MyLabel.Text = "Finished"
     ' ... (Synchronous Code)
 End Function   

然后使用DoWork方法执行相同的方法-始终使用Async方法返回Task UI事件处理程序是一个例外-与它们一起可以使用Async Sub
如果您不返回Task则您的方法将像“ Fire and Forget”一样工作,但是您不会注意到这些方法中引发了任何异常。

 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   

然后在事件处理程序中

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

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM