如何加快启动画面的加载速度

发布于 2024-08-26 05:09:35 字数 163 浏览 3 评论 0原文

我正在优化 WinForms 应用程序的启动。我发现的一个问题是启动屏幕表单的加载。大约需要半秒到一秒。

我知道多线程在 UI 部分是禁忌,但是,看看启动屏幕如何成为应用程序的一个相当自治的部分,是否有可能通过将其扔到另一个线程(也许在Chrome 的做法),以便应用程序的重要部分能够真正运行。

I am optimizing the startup of a WinForms app. One issue I identified is the loading of the splash screen form. It takes about half a second to a second.

I know that multi-threading is a no-no on UI pieces, however, seeing how the splash screen is a fairly autonomous piece of the application, is it possible to somehow mitigate its performance hit by throwing it one some other thread (perhaps in the way Chrome does it), so that the important pieces of the application can actually get going.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(6

作业与我同在 2024-09-02 05:09:35

.NET 框架已经对 Windows 窗体应用程序中的启动屏幕提供了很好的支持。检查此线程获取代码示例。它确实针对热启动时间进行了优化,它确保启动线程和屏幕在初始化主应用程序之前启动并运行。

The .NET framework already has very good support for splash screens in Windows Forms apps. Check this thread for a code sample. It is indeed optimized for warm startup time, it makes sure the splash thread and screen is up and running before initializing the main app.

假面具 2024-09-02 05:09:35

如果您的目标是尽快启动启动屏幕,则生成线程不会有任何好处。

有几种方法可以实现启动画面,这里提到了一种更复杂的方法,但是这个是一个简单的方法,我已经完全成功地使用了:

只需确保首先加载并显示启动表单,然后在用户查看漂亮的启动屏幕时继续加载您的应用程序。当主窗体加载完成后,它可以在显示自身之前关闭启动画面(一种简单的方法是将启动画面传递到其构造函数中的主窗体):

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();
    }
}

此线程,此解决方案的潜在缺点是用户无法与启动屏幕交互。然而,这通常不是必需的。

There's nothing to be gained from spawning a thread if your goal is to get the splash screen up as quickly as possible.

There are several ways to do splash screens, and a more sophisticated one is mentioned here, but this is an easy method I have used with complete success:

Just ensure you load and show the splash form first, and then continue to load your app while the user is looking at the pretty splash screen. When the mainform is done loading, it can close the splash right before it shows itself (a simple way to do this is pass the splash form to the mainform in its constructor):

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();
    }
}

Alternative: If you prefer not to pass the splash to the MainForm (maybe it seems inelegant), then subscribe to the MainForm's Load event, and close the splash screen there:

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();
    }
}

As mentioned in this thread, the potential downside to this solution is that the user won't be able to interact with the splash screen. However, that usually isn't required.

时光暖心i 2024-09-02 05:09:35

只要所有 UI 都保留在一个线程上,WinForms 中的多线程就可以了。

这就是启动画面通常的完成方式。重要的工作是在后台线程上完成的,而闪屏窗口则显示在 UI 线程上,让用户知道程序的其余部分很快就会出现。

重要的事情发生后,引发一个事件,让 UI 线程知道是时候隐藏闪屏了(只需记住使用 Invoke() 将事件处理程序编组回 UI 线程,以便关闭闪屏屏幕)。

Multithreading in WinForms is okay as long as all the UI stays on one thread.

This is just how splash screens are usually done. The important work is done on a background thread, while the splash screen window is shown on the UI thread to let the user know that the rest of the program will appear soon.

After the important stuff has happened, raise an event to let the UI thread know that it is time to hide the splash screen (just remember to marshal the event handler, using Invoke(), back onto the UI thread in order to close the splash screen).

一瞬间的火花 2024-09-02 05:09:35

是的。

您需要创建一个新的 STA 线程,使用 Application.Run 显示启动屏幕,然后在主窗体准备就绪后使用 Invoke 调用 Close (在主线程上)。

编辑:例如:

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 将在该方法开始执行之前加载它使用的所有类型)

Yes.

You need to make a new STA thread that shows the splash screen using Application.Run, then call Close using Invoke after the main form is ready (on the main thread).

EDIT: For example:

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));

For optimal performance, you should make your Main method show the splash screen, then call a separate method that loads the application. This way, all of the assemblies will be loaded after the splash screen is shown. (When you call a method, the JITter will load all of the types that it uses before the method starts executing)

沩ん囻菔务 2024-09-02 05:09:35

答案实际上与感知有关。有多种方法,NGEN 一个程序集,将东西放入 GAC 中,但您应该了解真正发生的事情。

C# 需要时间来加载虚拟机、加载引用的程序集以及根据启动屏幕上的内容加载程序集。然后从“冷”启动到启动第一个屏幕仍然需要一段时间。

这是因为当您第一次访问屏幕时正在进行 JIT 编译。

我使用的策略是传统的轻量级启动页面,它加载速度快,因此可见,但在后台我生成一个线程并加载不可见的表单。该表单包含我打算使用的所有控件,因此 JIT 编译器正在执行其操作并加载程序集。这提供了轻微的手部反应的错觉。从启动 + 可见的初始页面 + 暂停 + 单击第一个选项所需的时间大于线程执行然后清理和卸载表单所需的时间。

否则,应用程序在首次启动时对用户来说会显得笨重且缓慢。屏幕的热启动速度要快得多,因为程序集和 JIT 已经完成。

The answer is really about perception. There are various methods, NGEN an assembly, putting things in the GAC, but what you should understand is what is really going on.

C# require time to load the virtual machine, load referenced assemblies, and load assemblies based upon what is on that splash screen. And then it still takes awhile from a "cold" start to launch the first screen.

This is because of the JIT compiling that is going on when you first access a screen.

A strategy that I use is a traditional lightweight splash page that loads quickly so its visible, but in the background I spawn a thread and load an invisible form. This form has all the controls that I intend to use and so the JIT compiler is doing its thing and its loading the assemblies. This provides the illusion of responsiveness with slight of hand. The time it takes from launch + visible splash page + pause + time to click 1st option is greater then the time it takes for the thread to execute and then cleanup and unload the form.

Otherwise, applications appear to be clunky and slow for users when it first starts. Warm startup of screens is much faster because the assemblies and JIT has finished.

遗弃M 2024-09-02 05:09:35

我们通过提供一个小型本机 C++ 应用程序作为启动屏幕来实现这一点。

然后遵循以下过程:

  1. 用户双击应用程序图标。
  2. C++ 应用程序启动,将启动位图显示为 WS_TOPMOST。
  3. C++ 应用程序启动主 C# 应用程序。
  4. C# 应用程序启动,最后通知 C++ 应用程序(通过一个简单的文件)退出。
  5. C++ 应用程序退出。

C++ 应用程序还具有超时功能(以防 C# 应用程序崩溃),如果在 30 秒内未收到退出通知,则会自动退出。

这种方法有其缺点,因为您无法将应用程序固定到任务栏。如果您固定 C++ 应用程序(这是最终用户的主应用程序),您会在任务栏上看到另一个任务,因为 C# 应用程序不同。我认为,我可以通过在 应用程序清单,指示它们在任务栏方面是“相同”的应用程序。

We do it by providing a tiny small native C++ application that serves as the splash screen.

It then follows this process:

  1. User double clicks the application icon.
  2. The C++ app starts, showing a splash bitmap as WS_TOPMOST.
  3. The C++ app launches the main C# app.
  4. The C# app starts, finally notifying the C++ app (through a simple file) to quit.
  5. The C++ app quits.

The C++ application also has a timeout (in case the C# app crashes) and quits automatically if it does not get notified to quit within 30 seconds.

This approach has its drawbacks in the fact that you cannot pin the application to the task bar. If you pin the C++ app (which is the main app for the end user), you get another task on the task bar, because the C# app is different. I think, I could solve this by providing settings in the application manifest of both the C++ and C# application to instruct them to be the "same" application in terms of the task bar.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文