BETA

[WinForm] フォームクローズ中に「タスク終了待ち」をする

投稿日:2020-06-25
最終更新:2020-06-26

フォームクローズ中に「タスクの終了待ち」を行ったらデッドロックしたので、処理を考えてみました。

条件: タスクの中でUIにアクセスしている。

(1)「Invoke」や「BeginInvoke+EndInvoke」はフォームクローズ中に使うとデッドロックする
(2) タスクの終了を「await」を使用せず、敢えて「Result や Await」でUIスレッドを止めつつ待つ

public partial class frmMain : Form  
{  
    private Task<string> _task;  
    private CancellationTokenSource _tokenSource;  

    public frmMain()  
    {  
        InitializeComponent();  
    }  

    private void btnRun_Click(object sender, EventArgs e)  
    {  
        txtLog.Text = "AsyncFunc: Run.";  

        _tokenSource = new CancellationTokenSource();  

        _task = Task.Run(() => AsyncFunc(_tokenSource.Token), _tokenSource.Token);  

        btnRun.Enabled = false;  
        btnClose.Enabled = true;  
    }  

    private void btnClose_Click(object sender, EventArgs e)  
    {  
        btnRun.Enabled = true;  
        btnClose.Enabled = false;  

        txtLog.Text = "AsyncFunc: Closing.\r\n" + txtLog.Text;  

        Close();  
    }  

    private string AsyncFunc(CancellationToken token)  
    {  
        try  
        {  
            var count = 0;  
            var log = txtLog.Text;  

            while (token.IsCancellationRequested == false)  
            {  
                var handle = BeginInvoke(new Action(() => // (1)  
                {  
                    txtLog.Text = "AsyncFunc: Loop(" + count++ + ").\r\n" + log;  
                    txtLog.Update();  
                }));  

                if (handle.AsyncWaitHandle.WaitOne(300) == false) // (1)  
                {  
                    if (token.IsCancellationRequested == true)  
                    {  
                        // タスクのキャンセル中にここへ来るのは正しい  
                    }  
                    else  
                    {  
                        throw new Exception();  
                    }  
                }  
            }  

            var handle2 = BeginInvoke(new Action(() => // (1)  
            {  
                txtLog.Text = "AsyncFunc: Finish.\r\n" + txtLog.Text;  
                txtLog.Update();  
            }));  

            if (handle2.AsyncWaitHandle.WaitOne(300) == false) // (1)  
            {  
                if (token.IsCancellationRequested == true)  
                {  
                    // タスクのキャンセル中にここへ来るのは正しい  
                }  
                else  
                {  
                    throw new Exception();  
                }  
            }  
        }  
        catch (Exception ex)  
        {  
            Debug.WriteLine(ex.Message);  
        }  

        return "AsyncFunc: Success.";  
    }  

    private void frmMain_FormClosed(object sender, FormClosedEventArgs e)  
    {  
        _tokenSource.Cancel();  

        var result = _task.Result; // (2)  

        txtLog.Text = result + "\r\n" + txtLog.Text;  
        txtLog.Update();  

        txtLog.Text = "AsyncFunc: Closed.\r\n" + txtLog.Text;  
        txtLog.Update();  

        { // この部分は必要ないが、閉じる瞬間を目視するために追加しています。  
            var count = 5;  
            var log = txtLog.Text;  
            do  
            {  
                txtLog.Text = count + "\r\n" + log;  
                txtLog.Update();  

                Thread.Sleep(1000);  

            } while (count-- > 0);  
        } // ここまで  
    }  
}  

※希望通りの動作にはなったが、これが本当に正しい書き方なのかはわかりません...

技術ブログをはじめよう Qrunch(クランチ)は、プログラマの技術アプトプットに特化したブログサービスです
駆け出しエンジニアからエキスパートまで全ての方々のアウトプットを歓迎しております!
or 外部アカウントで 登録 / ログイン する
クランチについてもっと詳しく

この記事が掲載されているブログ

@u4xiの技術ブログ

よく一緒に読まれる記事

0件のコメント

ブログ開設 or ログイン してコメントを送ってみよう