- A+
https://www.codenong.com/cs106719464/
WinForm中的UI假死其实是个老生常谈的问题了,但最近还是很多人问我该如何解决,所以今天就来说明一下如何解决UI假死的问题。实验程序界面如下图所示:
方法一:async + await + Task
首先看下面一段代码:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 |
using System;
using System.Collections.Generic; using System.ComponentModel; using System.Data; using System.Drawing; using System.Linq; using System.Text; using System.Threading; using System.Threading.Tasks; using System.Windows.Forms; namespace WindowsFormsApplication1 // 开始 // 一个耗时任务 |
在上面的代码中,GetMessage()方法耗时10秒钟,如果你点击按钮,那么在10秒钟内窗体将处于假死状态。这种情况很常见,之所以会造成UI假死的原因也很简单:某个函数耗时太久。在遇见这种情况的时候,我们就可以考虑使用async + await + Task来解决,代码如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 |
using System;
using System.Collections.Generic; using System.ComponentModel; using System.Data; using System.Drawing; using System.Linq; using System.Text; using System.Threading; using System.Threading.Tasks; using System.Windows.Forms; namespace WindowsFormsApplication1 // 开始 // 一个耗时任务 |
运行之后点击按钮,你会发现UI没有假死,窗体可以随意拖动了。
方法二:使用BackgroundWorker组件
在很多时候,我们需要动态显示当前的程序执行进度,以便让用户了解程序已经执行到哪一步了。很多同志都会这么写:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 |
using System;
using System.Collections.Generic; using System.ComponentModel; using System.Data; using System.Drawing; using System.Linq; using System.Text; using System.Threading; using System.Threading.Tasks; using System.Windows.Forms; namespace WindowsFormsApplication1 // 开始 |
功能确实是实现了,进度条能够显示当前执行的进度,可惜UI还是处于假死状态,所以用户体验还是不好。其实WinForm已经给我们提供了一个处理多线程任务的组件BackgroundWorker,使用它可以轻松让你的程序告别UI假死,如下图所示:
代码如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 |
using System;
using System.Collections.Generic; using System.ComponentModel; using System.Data; using System.Drawing; using System.Linq; using System.Text; using System.Threading; using System.Threading.Tasks; using System.Windows.Forms; namespace WindowsFormsApplication1 // 开始 // DoWork // ProgressChanged // RunWorkerCompleted |
运行程序点击按钮,你会发现UI没有处于假死状态,窗体可以随意拖动。
方法三:Task + 委托(回调函数)
首先需要明确一点:UI线程位于主线程,如果想要在子线程里更新UI状态,必须要将其切换到主线程,最后进行更新操作。UI控件一般会提供Invoke、InvokeRequired,其中InvokeRequired用于判断是否有子线程在更新UI控件,如果有则返回true,Invoke用于将控制权切换到UI线程,代码如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 |
using System;
using System.Collections.Generic; using System.ComponentModel; using System.Data; using System.Drawing; using System.Linq; using System.Text; using System.Threading; using System.Threading.Tasks; using System.Windows.Forms; namespace WindowsFormsApplication1 // 开始 // 处理线程 |
方法三也可以解决UI的假死问题,当然也不一定要用Task,利用Thread也可以实现一样的效果。