我正在优化WinForms应用的启动。我发现的一个问题是加载了启动画面。它需要大约半秒到一秒。
我知道多线程在UI片段上是禁止的,然而,看到启动画面是一个相当自主的应用程序片段,是否有可能通过抛出一个其他线程来某种程度上减轻其性能损失(也许就像Chrome那样),这样应用程序的重要部分就可以实现。
答案 0 :(得分:9)
.NET框架已经非常好地支持Windows窗体应用程序中的启动画面。检查this thread以获取代码示例。它确实针对热启动时间进行了优化,它确保在初始化主应用程序之前启动并运行启动线程和屏幕。
答案 1 :(得分:3)
如果您的目标是尽快获得启动画面,则无法从产生线程中获得任何好处。
有多种方法可以进行初始屏幕,而且提到了一个更复杂的方法here,但这是一个我用过的简单方法,取得了圆满成功:
只需确保您加载并显示第一个的启动表单,然后在用户查看漂亮的启动画面时继续加载您的应用。当mainform完成加载时,它可以在它自己显示之前关闭启动(一个简单的方法是将启动窗体传递给构造函数中的mainform):
static void Main()
{
Application.SetCompatibleTextRenderingDefault(false);
SplashForm splash = new SplashForm();
splash.Show();
splash.Refresh(); // make sure the splash draws itself properly
Application.EnableVisualStyles();
Application.Run(new MainForm(splash));
}
public partial class MainForm : Form
{
SplashForm _splash;
public MainForm(SplashForm splash)
{
_splash = splash;
InitializeComponent();
}
protected override void OnLoad(EventArgs e)
{
base.OnLoad(e);
// or do all expensive loading here (or in the constructor if you prefer)
_splash.Close();
}
}
替代方法:如果您不想将闪存传递给MainForm(可能看起来不太优雅),那么订阅MainForm的Load事件,并关闭那里的启动画面:
static class Program
{
static SplashForm _splash;
[STAThread]
static void Main()
{
Application.SetCompatibleTextRenderingDefault(false);
_splash = new SplashForm();
_splash.Show();
_splash.Refresh();
Application.EnableVisualStyles();
MainForm mainForm = new MainForm();
mainForm.Load += new EventHandler(mainForm_Load);
Application.Run(mainForm);
}
static void mainForm_Load(object sender, EventArgs e)
{
_splash.Dispose();
}
}
如this thread中所述,此解决方案的潜在缺点是用户无法与启动画面进行交互。但是,这通常不是必需的。
答案 2 :(得分:1)
是
您需要创建一个使用Application.Run
显示启动画面的新STA线程,然后在主窗体准备好后(在主线程上)使用Close
调用Invoke
。 / p>
编辑:例如:
static SplashForm splash;
Thread splashThread = new Thread(delegate() {
splash = new SplashForm();
Application.Run(splash); //Blocking call on separate thread
});
splashThread.SetApartmentState(ApartmentState.STA)
splashThread.Start();
LoadApp();
//In MainForm_Shown:
splash.BeginInvoke(new Action(splash.Close));
为获得最佳性能,您应该使Main
方法显示启动屏幕,然后调用加载应用程序的单独方法。这样,在显示初始屏幕后将加载所有程序集。 (当您调用方法时,JITter将在方法开始执行之前加载它使用的所有类型)
答案 3 :(得分:1)
只要所有UI都停留在一个线程上,WinForms中的多线程就可以了。
这就是通常如何完成启动画面。重要的工作是在后台线程上完成的,而启动画面窗口显示在UI线程上,让用户知道程序的其余部分很快就会出现。
重要的事情发生之后,举起一个事件让UI线程知道是时候隐藏启动画面了(只记得用事件处理程序封送,使用Invoke(),回到UI线程,以便关闭闪屏)。
答案 4 :(得分:1)
答案实际上是关于感知。有各种各样的方法,NGEN是汇编,把事情放在GAC中,但你应该理解的是真正发生的事情。
C#需要时间来加载虚拟机,加载引用的程序集,并根据启动屏幕上的内容加载程序集。然后从“冷”开始到启动第一个屏幕仍然需要一段时间。
这是因为您首次访问屏幕时正在进行JIT编译。
我使用的策略是传统的轻量级启动页面,它可以快速加载以使其可见,但在后台我生成一个线程并加载一个不可见的表单。这个表单包含我打算使用的所有控件,因此JIT编译器正在执行它的操作并加载程序集。这提供了轻微响应的错觉。从启动+可见启动页面+暂停+时间到单击第一个选项所花费的时间大于线程执行然后清理和卸载表单所花费的时间。
否则,应用程序在首次启动时看起来很笨拙且速度很慢。由于装配和JIT已经完成,因此屏幕的热启动速度要快得多。
答案 5 :(得分:1)
我们通过提供一个小型的原生C ++应用程序来充当启动屏幕。
然后遵循这个过程:
C ++应用程序也有超时(如果C#应用程序崩溃)并在30秒内没有得到通知退出时自动退出。
这种方法的缺点在于您无法将应用程序固定到任务栏。如果您固定C ++应用程序(这是最终用户的主要应用程序),您将在任务栏上获得另一项任务,因为C#应用程序是不同的。我想,我可以通过在C ++和C#应用程序的application manifest中提供设置来解决这个问题,以指示它们在任务栏方面是“相同”的应用程序。