読者です 読者をやめる 読者になる 読者になる

おいしいとこはすこしだけ

文系出身SE見習いの備忘録。

マルチスレッド(Task)を使ってスプラッシュウィンドウを作成してみる

文系出身SE見習いの北白川キリコです。


起動時のファイル読み込みに時間が掛かるときのためにスプラッシュウィンドウを表示させよう! と思ってググってみると、マルチスレッドを使うといいという。

こちとらプログラミング歴3ヶ月ぐらいだしC#なんか1ヶ月もやってないのでマルチスレッドとか言われても困るんだけど、とりあえず分かりそうな範囲でやってみることに。


最初に参考にしたのはここ。

[C#] 別スレッドでスプラッシュウィンドウを表示する

で、見よう見まねで書いてみたんだけど、確かにファイル読み込みに一定の時間がかかる場合は上手くいくんだが、ファイルが軽くて一瞬で読み込み終了してしまうような場合は、スプラッシュウィンドウが開く前に閉じようとさせてしまうようで例外を吐く。

上手いことwaitをかけないといけないんだな……うーん……とか悩みつつ引き続き色々調べていたら、

こんなんが出てきた。ん? .NET4.0以降なら、Threadをシコシコ作るよりもっといい方法があるのね。


で書いてみたコードがこれ。

using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading;
using System.Windows.Forms;

namespace SplashWindowTest
{
    static class Program
    {
        /// <summary>
        /// アプリケーションのメイン エントリ ポイントです。
        /// </summary>
        [STAThread]
        static void Main()
        {
            Application.EnableVisualStyles();
            Application.SetCompatibleTextRenderingDefault(false);

            // 別スレッドで重い処理を実行
            var heavyTask = System.Threading.Tasks.Task.Factory.StartNew(() =>
            {
                Thread.Sleep(5000);    // 時間のかかる処理
            });
            
            // その間表示しておくスプラッシュウィンドウを作成
            SplashWindow splashWindow = new SplashWindow();
            // heavyTaskが終わるとスプラッシュウィンドウを閉じるよう
            // ContinueWithで予約しておく
            heavyTask .ContinueWith((x) =>
            {
                splashWindow.Invoke(new SplashClosing(splashWindow.Dispose));
            });
            
            // 予約しておいて後から開く
            splashWindow.ShowDialog();

            // 処理が終わったら=スプラッシュウィンドウが閉じたらメインダイアログを開く
            Application.Run(new MainDialog());
        }

        // フォームにアクセスする際のInvoke用にデリゲートを宣言しておく
        delegate void SplashClosing();
    }
}

こんな感じ?


ただこれだと、スプラッシュウィンドウが閉じられてしまうと(フォームデザインで普通には閉じられないようにできるが、閉じないわけではない)処理が途中で終わってしまう。

スプラッシュウィンドウ側のイベントハンドラで、フォームが閉じる際に処理が終わっているかどうかの判定を行うようにさせないといけないっぽいのかな。


「いやこれだとちゃんとマルチスレッドできてないぞ」とか「もっと上手な書き方があるよ」とかあったら教えてください。