WinForms アプリの起動を最適化しています。私が特定した 1 つの問題は、スプラッシュ スクリーン フォームの読み込みです。0.5 秒から 1 秒ほどかかります。
マルチスレッドは UI の部分ではダメだということはわかっていますが、スプラッシュ スクリーンがアプリケーションのかなり自律的な部分であることを考えると、別のスレッド (おそらくアプリケーションの重要な部分が実際に動作するようにします。
WinForms アプリの起動を最適化しています。私が特定した 1 つの問題は、スプラッシュ スクリーン フォームの読み込みです。0.5 秒から 1 秒ほどかかります。
マルチスレッドは UI の部分ではダメだということはわかっていますが、スプラッシュ スクリーンがアプリケーションのかなり自律的な部分であることを考えると、別のスレッド (おそらくアプリケーションの重要な部分が実際に動作するようにします。
.NET フレームワークでは、Windows フォーム アプリのスプラッシュ スクリーンが既に非常に適切にサポートされています。コードサンプルについては、このスレッドを確認してください。実際、ウォーム スタートアップ時間用に最適化されており、メイン アプリを初期化する前に、スプラッシュ スレッドと画面が起動して実行されていることを確認します。
できるだけ早くスプラッシュ スクリーンを表示することが目標である場合、スレッドを生成しても何も得られません。
スプラッシュ スクリーンを作成するにはいくつかの方法があり、より洗練された方法をここで説明しますが、これは私が使用した簡単な方法で完全に成功しています。
最初にスプラッシュ フォームを読み込んで表示してから、ユーザーがきれいなスプラッシュ スクリーンを見ている間にアプリを読み込んでください。メインフォームの読み込みが完了すると、それ自体が表示される直前にスプラッシュを閉じることができます (これを行う簡単な方法は、スプラッシュ フォームをコンストラクターでメインフォームに渡すことです)。
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();
}
}
このスレッドで述べたように、このソリューションの潜在的な欠点は、ユーザーがスプラッシュ スクリーンを操作できないことです。ただし、通常は必要ありません。
答えは、実際には知覚に関するものです。NGEN をアセンブリに入れたり、GAC に入れたり、さまざまな方法がありますが、実際に何が行われているのかを理解しておく必要があります。
C# では、仮想マシンの読み込み、参照アセンブリの読み込み、スプラッシュ スクリーンの内容に基づくアセンブリの読み込みに時間がかかります。そして、「コールド」スタートから最初の画面が起動するまでにはまだしばらく時間がかかります。
これは、最初に画面にアクセスしたときに JIT コンパイルが行われるためです。
私が使用する戦略は、高速に読み込まれる従来の軽量のスプラッシュ ページですが、バックグラウンドでスレッドを生成し、目に見えないフォームを読み込みます。このフォームには、私が使用する予定のすべてのコントロールが含まれているため、JIT コンパイラはその処理を実行し、アセンブリをロードしています。これにより、わずかな手で応答性があるという錯覚が得られます。起動 + 表示されたスプラッシュ ページ + 一時停止 + 最初のオプションをクリックするまでの時間は、スレッドの実行、クリーンアップ、およびフォームのアンロードにかかる時間よりも長くなります。
そうしないと、アプリケーションを最初に起動したときに、ユーザーにとって、アプリケーションがぎこちなく遅く見えるようになります。アセンブリと JIT が完了しているため、画面のウォーム スタートアップははるかに高速です。
これは、スプラッシュ スクリーンとして機能する小さなネイティブ C++ アプリケーションを提供することで実現します。
次に、次のプロセスに従います。
C++ アプリケーションにはタイムアウト (C# アプリがクラッシュした場合) もあり、30 秒以内に終了するように通知されない場合は自動的に終了します。
このアプローチには、アプリケーションをタスク バーに固定できないという欠点があります。C++ アプリ (エンド ユーザーのメイン アプリ) を固定すると、C# アプリが異なるため、タスク バーに別のタスクが表示されます。C++ と C# の両方のアプリケーションのアプリケーション マニフェストに設定を提供して、タスク バーに関して "同じ" アプリケーションになるように指示することで、これを解決できると思います。
はい。
を使用してスプラッシュ スクリーンを表示する新しい STA スレッドを作成し、(メイン スレッドで) メイン フォームの準備が整った後にusing を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 は使用するすべての型をロードします)
すべての UI が 1 つのスレッド上にある限り、WinForms でのマルチスレッド化は問題ありません。
これは、スプラッシュ スクリーンが通常行われる方法です。重要な作業はバックグラウンド スレッドで行われますが、スプラッシュ スクリーン ウィンドウは UI スレッドに表示され、プログラムの残りの部分がすぐに表示されることをユーザーに知らせます。
重要なことが起こった後、イベントを発生させて、スプラッシュ スクリーンを非表示にする時間になったことを UI スレッドに知らせます (スプラッシュを閉じるために、Invoke() を使用してイベント ハンドラーを UI スレッドにマーシャリングすることを忘れないでください)。画面)。