vb.net 如何防止UI在漫长的过程中冻结?

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/2447123/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-09-09 14:46:34  来源:igfitidea点击:

How to prevent UI from freezing during lengthy process?

vb.netuser-interface

提问by Gulbahar

I need to write a VB.Net 2008 applet to go through all the fixed-drives looking for some files. If I put the code in ButtonClick(), the UI freezes until the code is done:

我需要编写一个 VB.Net 2008 小程序来遍历所有固定驱动器以查找一些文件。如果我将代码放在 ButtonClick() 中,则 UI 会冻结,直到代码完成:

Private Sub Button1_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Button1.Click
    'TODO Find way to avoid freezing UI while scanning fixed drives

    Dim drive As DriveInfo
    Dim filelist As Collections.ObjectModel.ReadOnlyCollection(Of String)
    Dim filepath As String

    For Each drive In DriveInfo.GetDrives()
        If drive.DriveType = DriveType.Fixed Then
            filelist = My.Computer.FileSystem.GetFiles(drive.ToString, FileIO.SearchOption.SearchAllSubDirectories, "MyFiles.*")
            For Each filepath In filelist
                'Do stuff
            Next filepath
        End If
    Next drive
End Sub

Google returned information on a BackGroundWorker control: Is this the right/way to solve this issue? If not, what solution would you recommend, possibly with a really simple example?

Google 返回有关 BackGroundWorker 控件的信息:这是解决此问题的正确方法/方法吗?如果没有,您会推荐什么解决方案,可能是一个非常简单的例子?

FWIW, I read that Application.DoEvents() is a left-over from VBClassic and should be avoided.

FWIW,我读到 Application.DoEvents() 是 VBClassic 的遗留物,应该避免。

Thank you.

谢谢你。

回答by Klaus Byskov Pedersen

The BackgroundWorkeris a good way to solve your problem. Actually the documentation states this:

BackgroundWorker是解决您问题的好方法。实际上,文档说明了这一点:

The BackgroundWorker class allows you to run an operation on a separate, dedicated thread. Time-consuming operations like downloads and database transactions can cause your user interface (UI) to seem as though it has stopped responding while they are running. When you want a responsive UI and you are faced with long delays associated with such operations, the BackgroundWorker class provides a convenient solution.

BackgroundWorker 类允许您在单独的专用线程上运行操作。下载和数据库事务等耗时操作可能会导致您的用户界面 (UI) 看起来好像在它们运行时已停止响应。当您需要响应式 UI 并且面临与此类操作相关的长时间延迟时,BackgroundWorker 类提供了一个方便的解决方案。

回答by TomTom

Put the process into a separate thread.... ...using the BackgroundWorker component.

将进程放入一个单独的线程.... ...使用 BackgroundWorker 组件。

Disable UI components that should not be usable while the process workd.

禁用进程工作时不应使用的 UI 组件。

Finished - the UI will still be responsive.

完成 - 用户界面仍将响应。

回答by M.A. Hanin

The key is to seperate the UI code from the actual functionality code. The time-consuming functionality should run on a seperate thread. To achieve this, you can either:

关键是将 UI 代码与实际功能代码分开。耗时的功能应该在单独的线程上运行。为此,您可以:

  1. Create and start a Threadobject by yourself
  2. Create a Delegateand use asynchronous invokation (using BeginInvoke).
  3. Create and start a BackgroundWorker.
  1. Thread自己创建和启动一个对象
  2. 创建一个Delegate并使用异步调用(使用 BeginInvoke)。
  3. 创建并启动一个BackgroundWorker.

As you mentioned, you should avoid Application.DoEvents(). A proper breakdown of the application's functionality will allow you to create an application which is designed to be responsive, rather than creating a non-responsive application with DoEvents "fixes" (which is costly, considered bad practice, and implies a bad design).

正如你提到的,你应该避免Application.DoEvents(). 应用程序功能的正确分解将允许您创建一个旨在响应的应用程序,而不是创建一个带有 DoEvents“修复”的无响应应用程序(这很昂贵,被认为是不好的做法,并且意味着不好的设计)。

Since your method doesn't return a value and doesn't update the UI, the fastest solution might be creating a Delegate and using "fire and forget" asynchronous invokation:

由于您的方法不返回值并且不更新 UI,最快的解决方案可能是创建一个委托并使用“即发即弃”异步调用:

Private Sub Button1_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Button1.Click
    Call New Action(AddressOf DrivesIteration).BeginInvoke(Nothing, Nothing)
End Sub

Private Sub DrivesIteration()
    Dim drive As DriveInfo
    Dim filelist As Collections.ObjectModel.ReadOnlyCollection(Of String)
    Dim filepath As String

    For Each drive In DriveInfo.GetDrives()
        If drive.DriveType = DriveType.Fixed Then
            filelist = My.Computer.FileSystem.GetFiles(drive.ToString, FileIO.SearchOption.SearchAllSubDirectories, "MyFiles.*")
            For Each filepath In filelist
                DoStuff(...)
            Next
        End If
    Next 
End Sub

BTW, For..Next blocks no longer have to end with "Next (something)", it is obsolete - VB now infers the (something) by itself, so there is no need to state it explicitly.

顺便说一句,For..Next 块不再必须以“Next (something)”结尾,它已经过时了——VB 现在可以自行推断 (something),因此无需明确说明。

回答by tobrien

A. put up a PROGRESS BAR... update it and .REFRESH it ... If all you want is to show that your not dead.

A. 设置一个进度条...更新它并刷新它...如果你想要的只是表明你没有死。

B. DoEvents is evil sounds A LOT like "NEVER USE A GOTO..." pleeeeze pleeeze pleeeze there are times and circumstances where any language's syntax can be harmful AND helpful. Why jump through a million hoops just to essentially do "A" above?

B. DoEvents 听起来很邪恶,很像“永远不要使用 GOTO...” pleeeze pleeeze pleeeze 在某些时候和情况下,任何语言的语法都可能有害且有用。为什么跳过一百万个箍只是为了基本上做上面的“A”?

<soapbox>

<肥皂盒>

If you know that something takes a LONG TIME and you also know that no other operations can take place WHILE YOUR WAITING (i.e. it is essentially a serial process) than if you do ANYTHING like that and push it into "the background" then you'll be sprinkling "ITS_OK_TO_CONTINUE" booleans all through the rest of your code just waiting for the file process to end anyway.... whats the point of that? All you've done is complicate your code for the sake of... hmm... "good programming?" Not in my book.

如果您知道某件事需要很长时间,并且您也知道在您等待时(即它本质上是一个串行过程)不会发生任何其他操作,而不是执行类似的任何操作并将其推入“后台”,那么您将在你的代码的其余部分洒上“ITS_OK_TO_CONTINUE”布尔值,只是等待文件进程结束......这有什么意义?你所做的只是为了……嗯……“好的编程?”而使代码复杂化。我的书里没有。

Who cares if DoEvents is "left over" from the ICE AGE. Its EXACTLY the right thing in MANY circumstances. For example: The framework gives you ProgressBar.Refresh butyou'll see that its not exactly "working" unless you post-pend a few DoEvents after it.

谁在乎 DoEvents 是否是从冰河时代“遗留下来的”。它在许多情况下都是正确的。例如:该框架为您提供了 ProgressBar.Refresh您会看到它并不完全“工作”,除非您在它之后挂起一些 DoEvents。

</soapbox>

< /肥皂盒>

C. A background task is just that -- background; and you generally use it to operate on NON-SERIAL tasks or at least asynchronous tasks that MAY or MAY NOT update the foreground at some point. But I'd argue that anytime a so-called background task HALTS the foreground then it is (almost) by definition --- a FOREGROUND task; regardless of HOW LONG it takes.

C. 后台任务就是——后台;并且您通常使用它来操作非串行任务或至少异步任务,这些任务可能会或可能不会在某些时候更新前台。但我认为,任何时候所谓的后台任务都会暂停前台,那么它(几乎)根据定义——一个前台任务;不管需要多长时间。