简体   繁体   English

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

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

So, I have the following layout in a part of my program written in Visual Basic 2015: (the following is just pseudocode to make it easier to understand) 因此,在用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)
}

Now, what I wanted to accomplish is the following flow: 现在,我要完成的工作流程如下:

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.

Apparently, it seems that VB doesn't respect my flow, or I simply don't understand exactly how Await works, because every single time I run it, I get an exception below Await InitDB_t when I actually use the - assumed initialized - DB and upon checking, I realized to my surprise that InitDatabase() hasn't actually finished, but is still "awaiting" on SQLConnection.OpenAsync() , even though Await InitDB_t has returned!!! 显然,似乎VB不尊重我的流程,或者我只是不完全了解Await工作原理,因为每次运行它时,当我实际使用-假设已初始化Await InitDB_t时,都会在Await InitDB_t下得到异常经过检查,令我惊讶的是, InitDatabase()尚未真正完成,但是即使Await InitDB_t返回了,它仍然在SQLConnection.OpenAsync()上“等待”!

How can I alter my code, without getting a deadlock, since using Task.Wait() would actually block the current execution thread (and thus, I would be 100% sure that anything after it is executed after the DB has been fully initialized, but, oops, I would get a deadlock) and since I need contexts to update some GUI elements? 我如何才能在没有死锁的情况下更改代码,因为使用Task.Wait()实际上会阻塞当前执行线程(因此,我将百分百确定在数据库完全初始化之后,执行之后的所有操作,但是,糟糕,我会遇到死锁),并且由于我需要上下文来更新某些GUI元素?

Edit 1 : OK, after further debugging it seems that my logic is more wrong than I expected, since another exception arises: When I execute InitDatabase() I do some GUI assignments too, such as label_p.Content = "finished" which throws an exception because I am in a different thread than the GUI one. 编辑1 :好的,在进一步调试之后,我的逻辑似乎比我预期的要错误得多,因为会出现另一个异常:当我执行InitDatabase()我也进行了一些GUI分配,例如label_p.Content = "finished" ,该抛出例外,因为我与GUI不在同一线程中。 So, I would like corrections for both exceptions now... 因此,我现在想更正这两种例外情况...

I realized to my surprise that InitDatabase() hasn't actually finished, but is still "awaiting" on SQLConnection.OpenAsync(), even though Await InitDB_t has returned!!! 令我惊讶的是,InitDatabase()尚未真正完成,但是即使Await InitDB_t已经返回,它仍然在SQLConnection.OpenAsync()上“等待”!

This can happen if you use Async Sub . 如果使用Async Sub可能会发生这种情况。 You should only use Async Sub for event handlers. 您只应将Async Sub用于事件处理程序。

When I execute InitDatabase() I do some GUI assignments too, such as label_p.Content = "finished" which throws an exception because I am in a different thread than the GUI one. 当我执行InitDatabase()时,我也会做一些GUI分配,例如label_p.Content =“ finished”,这会引发异常,因为我所在的线程与GUI不同。

Remove the call to Task.Run . 删除对Task.Run的调用。 You don't need it anyway, since your code is asynchronous. 无论如何,您都不需要它,因为您的代码是异步的。

Your main problem is that you wrapped InitDatabase() with new thread. 您的主要问题是用新线程包装了InitDatabase() Task returned by Task.Run will completes before OpenAsync completes. Task.Run返回的Task.Run将在OpenAsync完成之前完成。

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

If InitDatabase is properly created asynchronous method (you didn't show full context) then it will return Task . 如果InitDatabase是正确创建的异步方法(您没有显示完整的上下文),则它将返回Task

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

And Task will be returned back to caller on Await SQLConnection.OpenAsync() line, when OpenAsync completes execution continue to the next line. OpenAsync完成执行后,继续执行下一行,然后Task将在OpenAsync Await SQLConnection.OpenAsync()行上返回给调用方。

So inside Task.Run this method will return value before OpenAsync completes. 因此,在Task.Run内部,此方法将在OpenAsync完成之前返回值。
Because method InitDatabase completes execution the status of task InitDB_t will be RanToCompletion too while actually OpenAsync not finished yet. 由于方法InitDatabase已完成执行,因此任务InitDB_t的状态InitDB_t将为RanToCompletion而实际上OpenAsync尚未完成。

Because all your method are asynchronous you don't need Task.Run at all and all operations can be executed on same thread - which will successfully updates UI controls. 由于所有方法都是异步的,因此根本不需要Task.Run ,并且所有操作都可以在同一线程上执行-这将成功更新UI控件。

First be sure that InitDatabase return Task . 首先,请确保InitDatabase返回Task I suggest to rename method to InitDatabaseAsync , suffix "Async" is added by conventions, but I found it very usefull. 我建议将方法重命名为InitDatabaseAsync ,约定中添加了后缀“ Async”,但我发现它非常有用。

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

Then same approach with DoWork method - always return Task with Async method. 然后使用DoWork方法执行相同的方法-始终使用Async方法返回Task Exception is UI eventhandlers - with them you can use Async Sub . UI事件处理程序是一个例外-与它们一起可以使用Async Sub
If you not returning a Task then your method will work like "Fire and Forget", but you will not notice any exception thrown inside those methods. 如果您不返回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   

Then in eventhandler 然后在事件处理程序中

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