本文具体根据异步加载大资源文件案例中的多线程变成学习
C# 中 BeginInvoke 与 EndInvoke 的一个简单的使用案例
在使用 WPF 开发桌面软件过程中,遇到一个需要预加载大量文件的需求,具体需求如下:
- 在软件启动后,需要加载大量的 CAD 资源文件,文件供软件中后续使用。
- 文件加载过程中非常耗时,若同步加载则界面会出现假死的状态。
- 在文件加载过程中,需要保证界面仍然可以进行其他操作,屏蔽跟资源文件有关的操作。
- 文件加载完成后,开放跟资源文件有关的操作。
解决方案:
在软件启动后,软件页面正常完成加载后,后台继续进行异步加载资源文件,此时可操作正在加载的资源文件的按钮设置为不可操作。文件加载完成之后开发操作资源文件的按钮可操作。
具体实现如下:
1、WPF 页面 Window 中存在一个事件:ContentRendered。该事件在窗口的内容呈现完毕之后发生。定义一个方法,方法中进行文件加载的操作,并将该方法绑定到 ContentRendered 事件。
代码如下:
其中,LoadingResources() 方法为实际加载文件的方法;
ShowProgressBarFile 方法中具体实现异步,并更改页面按钮状态
private void Window_ContentRendered(object sender, EventArgs e)
{
//禁用对应的操作按钮
this.btnThreeView.IsEnabled = false;
Func<bool> action = (() => LoadingResources());
ShowProgressBarFile(action, "文件加载中...");
}
2、方法 LoadingResources(),实现文件加载的功能。
文件加载使用多线程编程。一个线程为真加载线程,加载对应的资源文件,文件完成加载后,对应页面的操作按钮放开显示;
一个线程为假加载线程,可以用于加载进度条显示在页面。
具体详细的学习和分析见文章:
private bool LoadingResources()
{
//......实现具体文件加载
Thread t1 = new Thread(new ThreadStart(loadReal)); //loadReal 是真线程加载
t1.Name = "真加载线程";
t1.IsBackground = false;
Thread t2 = new Thread(new ThreadStart(loadNotTrue)); //loadNotTrue 是假线程加载
t2.Name = "假加载线程";
t2.IsBackground = true;
t1.Start();
t2.Start();
while (trueLoad == 0 && falseLoad == 0)
{
//阻塞
}
//下面的代码是在完成加载后,屏蔽页面假线程加载显示的进度条信息
this.pb_info.Dispatcher.Invoke(DispatcherPriority.Normal, (ThreadStart)delegate ()
{
this.pb_info.Visibility = Visibility.Collapsed;
});
this.pb_test.Dispatcher.Invoke(DispatcherPriority.Normal, (ThreadStart)delegate ()
{
this.pb_test.Visibility = Visibility.Collapsed;
});
return true;
}
3、方法ShowProgressBarFile 实现异步:
其中,func.BeginInvoke 开启异步;
asyncResult.IsCompleted 用于检查异步方法是否执行完成;
func.EndInvoke 结束异步。
这种异步编程的方法叫做“异步编程模型(APM)模式”
private bool ShowProgressBarFile(Func<bool> func, string text)
{
var asyncResult = func.BeginInvoke(null, null);
while (!asyncResult.IsCompleted)
{
System.Windows.Forms.Application.DoEvents();
}
return func.EndInvoke(asyncResult);
}