Как ускорить загрузку заставки - PullRequest
7 голосов
/ 25 марта 2010

Я оптимизирую запуск приложения WinForms. Одна из выявленных проблем - загрузка формы заставки. Это займет около полсекунды до секунды.

Я знаю, что многопоточность - это нет-нет для элементов пользовательского интерфейса, однако, увидев, что заставка является довольно автономной частью приложения, можно ли каким-то образом снизить ее производительность, перебросив ее в другой поток (возможно, в том, как это делает Chrome), так что важные части приложения действительно могут начать работу.

Ответы [ 6 ]

9 голосов
/ 25 марта 2010

.NET Framework уже имеет очень хорошую поддержку заставок в приложениях Windows Forms. Проверьте этот поток для образца кода. Он действительно оптимизирован для теплого времени запуска, он проверяет, что заставка и экран запущены и работают перед инициализацией основного приложения.

3 голосов
/ 25 марта 2010

Нечего получить от создания потока, если ваша цель - как можно быстрее открыть заставку.

Есть несколько способов создания заставок, и упоминается более сложный здесь , но это простой метод, который я использовал с полным успехом:

Просто убедитесь, что вы загружаете и показываете заставку сначала , а затем продолжаете загружать ваше приложение, пока пользователь смотрит на симпатичный экран заставки. Когда основная форма завершает загрузку, она может закрыть всплеск непосредственно перед тем, как она себя покажет (простой способ сделать это - передать форму всплеска главной форме в ее конструкторе):

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 (возможно, он выглядит не элегантным), подпишитесь на событие Load в MainForm и закройте там заставку:

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

Как уже упоминалось в этой теме , потенциальным недостатком этого решения является то, что пользователь не сможет взаимодействовать с заставкой. Однако обычно это не требуется.

1 голос
/ 11 ноября 2010

Мы делаем это, предоставляя крошечное маленькое собственное приложение C ++, которое служит заставкой.

Затем следует этот процесс:

  1. Пользователь дважды щелкает значок приложения.
  2. Запускается приложение C ++, показывающее растровое изображение-заставку как WS_TOPMOST.
  3. Приложение C ++ запускает главное приложение C #.
  4. Приложение C # запускается, наконец, уведомляя приложение C ++ (через простой файл) о выходе.
  5. Приложение C ++ закрывается.

Приложение C ++ также имеет тайм-аут (в случае сбоя приложения C #) и завершает работу автоматически, если оно не получает уведомление о выходе в течение 30 секунд.

Этот подход имеет свои недостатки в том, что вы не можете закрепить приложение на панели задач. Если вы закрепите приложение C ++ (которое является основным приложением для конечного пользователя), вы получите другую задачу на панели задач, потому что приложение C # отличается. Я думаю, что я мог бы решить эту проблему, предоставив настройки в манифесте приложения *1020* приложения C ++ и C #, чтобы они были «одинаковыми» в терминах панели задач.

1 голос
/ 25 марта 2010

Ответ действительно о восприятии. Существуют различные методы, сборка NGEN, помещение вещей в GAC, но вы должны понимать, что на самом деле происходит.

C # требуется время для загрузки виртуальной машины, загрузки сборок, на которые имеются ссылки, и загрузки сборок в зависимости от того, что находится на этом заставке. И затем все еще требуется некоторое время от «холодного» старта до запуска первого экрана.

Это из-за JIT-компиляции, которая происходит при первом доступе к экрану.

Стратегией, которую я использую, является традиционная облегченная заставка, которая быстро загружается, чтобы она была видимой, но на заднем плане я создаю поток и загружаю невидимую форму. В этой форме есть все элементы управления, которые я собираюсь использовать, и поэтому JIT-компилятор делает свое дело и загружает сборки. Это обеспечивает иллюзию отзывчивости с легкой руки. Время, необходимое для запуска 1-й опции + видимой заставки + паузы + времени для нажатия 1-го параметра, больше, чем время, необходимое для выполнения потока, а затем очистки и выгрузки формы.

В противном случае приложения кажутся неуклюжими и медленными для пользователей при первом запуске. Теплый запуск экранов намного быстрее, потому что сборки и JIT закончились.

1 голос
/ 25 марта 2010

Многопоточность в WinForms - это нормально, если весь пользовательский интерфейс остается в одном потоке.

Так обычно делают заставки. Важная работа выполняется в фоновом потоке, в то время как в потоке пользовательского интерфейса отображается окно заставки, чтобы пользователь знал, что скоро появится остальная часть программы.

После того, как произошли важные события, вызовите событие, чтобы поток пользовательского интерфейса узнал, что пришло время скрыть заставку (просто не забудьте перенаправить обработчик события, используя Invoke (), обратно в поток пользовательского интерфейса, чтобы закройте заставку).

1 голос
/ 25 марта 2010

Да.

Вам нужно создать новый поток STA, который показывает заставку, используя Application.Run, затем вызвать Close, используя Invoke после того, как основная форма готова (в главном потоке).

РЕДАКТИРОВАТЬ : Например:

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 загрузит все типы, которые он использует, прежде чем метод начнет выполняться)

...