vb.net - 创建一个等待延迟替代 Application.DoEvents
问题描述
多年来,我使用以下方法在我的软件中造成延迟:
Wait(10000)
Sub Wait(milliseconds)
<here I get the current time>
Do
<here I loop until the current time passed in seconds and then exit this loop>
Application.DoEvents()
Loop
End Sub
问题是,这会占用大量 CPU。我试过Thread.Sleep(1000)
了,但这会在我的应用程序执行时冻结我的应用程序!
我尝试使用计时器,但我仍然需要一个不会冻结但行为类似于 Application.DoEvents() 的循环。这似乎是不可能的。
我的目标是这样做:
label1.text = "ok about to start"
Wait(5000)
' the following line CAN NOT run until after 5 seconds.
label1.text = "DONE"
解决方案
延迟后如何执行代码。
有不同的方法可以在延迟后执行代码或异步执行代码,无论是否延迟。在这里,我正在考虑一个 Timer 和 Async/Await 模式的简单实现。
Application.DoEvent()
在任何情况下都应避免调用循环。
► 使用Timer以一种或多种方法延迟单个指令或代码的执行:
您不能等待 Timer,但您可以使用创建 Timer 并在 Timer 引发其事件时执行 Action 的方法,指示指定的 Interval 已过。
后面的方法接受一个delay
值和一个Action委托作为参数。
Delay 用于设置 Timer 的 Interval,Action 表示 Timer Ticks 时将执行的代码(我在这里使用System.Windows.Forms.Timer,因为您似乎指的是 WinForms 应用程序)。
Private waitTimer As New System.Windows.Forms.Timer()
Private TimerTickHandler As EventHandler
Private Sub Wait(delay As Integer, action As Action)
waitTimer.Interval = delay
TimerTickHandler = New EventHandler(
Sub()
action.Invoke()
waitTimer.Stop()
RemoveHandler waitTimer.Tick, TimerTickHandler
End Sub)
AddHandler waitTimer.Tick, TimerTickHandler
waitTimer.Start()
End Sub
当我们需要延迟执行代码时,我们可以调用这个方法。
Action 可以是一个简单的指令:在这种情况下,Text oflabel1
将在 5 秒后设置为“Done”,而 UI Thread 继续其操作:
label1.text = "About to Start..."
Wait(5000, Sub() Me.label1.Text = "Done")
Action 也可以是一个方法:
Private Sub SetControlText(control As Control, controlText As String)
control.Text = controlText
End Sub
' Elsewhere
Wait(5000, Sub() SetControlText(Me.label1, "Done"))
当然,该SetControlText()
方法可以执行更复杂的代码,并且可以选择设置 Timer 本身,调用Wait()
.
► 使用异步/等待模式。
异步方法提供了一种方便的方法来完成可能 长时间运行的工作,而不会阻塞调用者的线程。异步方法的调用者 无需等待异步 方法完成即可恢复其工作。
简单来说,将Async 修饰符添加到方法中,允许使用Await 运算符等待异步过程终止,然后再执行后面的代码,而当前线程可以自由地继续其处理。
▬ 请注意,Async
修饰符始终应用于Function()
返回 aTask
或 a 的 a Task(Of something)
(可以是方法可以返回的任何值/引用)。
它仅适用于( ) 方法表示aSub()
时的a 。毫无例外地记住和应用这一点非常重要(除非您非常清楚其中的含义)。▬</p>
Sub
void
Event Handler
阅读有关此的文档(在上一个链接中)和这些:
一个简单的Async
方法可以用来延迟一个动作的执行:
Private Async Function Wait(delay As Integer, action As Action) As Task
Await Task.Delay(delay)
action?.Invoke()
End Function
这与 Timer 功能类似,并且以类似的方式运行。不同之处在于,您可以使用Await
此方法执行异步运行的代码,并在方法返回后等待其完成运行其他代码。调用线程(此处为 UI 线程)将在Wait()
等待方法时继续其操作。
假设在Button.Click
处理程序中,我们要执行一个 Action(一个不返回值的方法)或一个 Function(一个返回值的方法),并且后面的代码应该只在这个 Action 或 Function 返回后执行:
Private Async Sub button1_Click(sender As Object, e As EventArgs) Handles button1.Click
Await Wait(5000, New Action(Sub() Me.label1.Text = "Done"))
Await Wait(5000, Nothing)
' (...)
' ... More code here. It will execute after the last Await completes
End Sub
这里我们指令等待5秒,然后给一个Label的Text设置一个值,再等5秒,什么都不做,然后执行下面的代码
如果我们不需要执行一个动作,我们可以简单地使用Task.Delay():
Private Async Sub button1_Click(sender As Object, e As EventArgs) Handles button1.Click
label1.text = "About to Start..."
Await Task.Delay(5000)
label1.Text = "Done"
' (...)
' ... More code here. It will execute after the last Await completes
End Sub
我们还可以使用 Async/Await 等待在ThreadPool Thread中运行的 Task 完成,调用Task.Run()来执行 Lambda 表达式中的代码:(
只是一个示例,我们不应该使用 ThreadPool Thread一个简单的任务)
Private Async Sub button1_Click(sender As Object, e As EventArgs) Handles button1.Click
label1.text = "About to Start..."
Await Task.Run(Async Function()
Await Task.Delay(5000)
BeginInvoke(New Action(Sub() Me.label1.Text = "Done"))
End Function)
' (...)
' ... More code here. It will execute after the last Await completes
End Sub
另请参阅Task.ContinueWith()方法:
创建一个在目标任务完成时异步执行的延续。
推荐阅读
- neo4j - 无法启用 Neo4j 空间扩展
- html - 带有 Ionic3 的 HTML,如何分隔行
- python - 为什么工作线程会削弱 GUI 响应能力?
- java - 如何使用改造 2 将空 JSON 数组字符串值替换为空字符串?
- spring - 错误 ohejsSqlExceptionHelper 连接已关闭
- typescript - 使用带有 TypeScript 的“emitDecoratorMetadata”选项的递归类时的 ReferenceError
- r - 在 R 中编码这个方程
- c# - 启动 Kestrel ASP.NET 服务器的证书问题 - Windows 10、C#、VS2019
- c# - 如何使用 Fluent Validator 为返回 Task 的属性设置自定义验证器
- python - 如何在 Python 列表中打印多个最大值(整数)?