###前提・実現したいこと
お世話になります。
C#でフォームを閉じるボタンを押した場合に、その時点で非同期で動作しているタスクAを終了させてから
フォームを閉じたいと思っています。
###発生している問題・エラーメッセージ
上記を以下の処理フローで実現しようとしました。
1.フォームを閉じるボタンを押す。
2.App_FormClosing(object sender, FormClosingEventArgs e)のイベントハンドラが呼ばれる。
3.その中でe.Cancel = true;をしてフォームを閉じるのをキャンセルする。
4.非同期タスクAをキャンセルしにいく。
5.非同期タスクAがキャンセルされるまで、キャンセル待ちタスクBを別途立ち上げて待つ。
6.非同期タスクAがキャンセルされると、キャンセル待ちタスクBが処理完了する。
7.e.Cancel = false;をしてフォームを閉じさせる。
しかし、結果はe.Cancel = false;が実行されているにも関わらずフォームは閉じませんでした。
###該当のソースコード
----------------------------------- 処理の流れ -----------------------------------
1.非同期タスクAが動作している状態で、フォームの閉じるボタンを押すとApp_FormClosing()が呼ばれる。
2.タスクAが完了状態になってからフォームを閉じたいので、App_FormClosing()内でe.Cancel = true;
により一旦閉じる処理をキャンセルする。
3.非同期タスクA:TaskA()は2s周期で何らかの処理を行っており、同時に
m_CTS.Token.IsCancellationRequestedによりタスクキャンセル命令が来ていないかチェックしている。
4.UI側からCancelTaskA()により、タスクAをキャンセルする。
直後にUI側ではキャンセル待ちタスクBを起動し、タスクAがキャンセルにより実行完了状態になるのを
GetIsTaskACompleted()でチェックしながら待つ。
5.UI側からのキャンセル命令によりタスクAにて、m_CTS.Token.IsCancellationRequestedがtrueになり、
タスクAは実行完了する。
6.タスクAの実行完了により、キャンセル待ちタスクBも待ち終了する。
7.e.Cancel = false;によりフォームを閉じる
----------------------------------- UI側 -----------------------------------
/**
@brief 閉じるボタンを押すと呼ばれるイベントハンドラ
*/
async void App_FormClosing(object sender, FormClosingEventArgs e) {
/* 閉じる処理をキャンセルする
非同期タスクAをキャンセルする
*/
e.Cancel = true;
CancelTaskA();
/* キャンセル待ちタスクBを起動する */
await Task.Factory.StartNew(() => {
while (true) {
Thread.Sleep(500);
/* 非同期タスクAがキャンセルにより実行完了すればキャンセル待ち終了 */
if (GetIsTaskACompleted()) {
break;
}
}
});
/* フォームを閉じる */
e.Cancel = false;
}
----------------------------------- 非同期タスクA側 -----------------------------------
/**
@brief 非同期タスクA用キャンセルトークンソース
*/
private CancellationTokenSource m_CTS new CancellationTokenSource();
/**
@brief 非同期タスクA
*/
private async Task TaskA()
{
await Task.Factory.StartNew(() => {
while (true) {
if (m_CTS.Token.IsCancellationRequested) {
break;
}
Thread.Sleep(2000);
何らかの処理;
}
}, m_CTS.Token);
}
/**
@brief 非同期タスクAをキャンセルする
*/
public void CancelAllTask()
{
m_CTS.Cancel();
}
/**
@brief 非同期タスクAが実行完了したかどうかを得る
*/
public bool GetIsTaskACompleted()
{
return m_TaskA.IsCompleted;
}
###試したこと
非同期タスクAはCancelTaskA()によりキャンセルされ、実行完了し、GetIsTaskACompleted()により
正しく実行完了が検知されることは確認しました。
その後、e.Cancel = false;も実行されるのですがフォームが閉じません。
App_FormClosing()はUIスレッドが呼ぶ関数ですが、キャンセル待ちタスクBの実行でタスクコンテキストが
そちらに移行し、タスクB完了時にもコンテキストがUIに戻っていない?ためにe.Cancel = false;でも
フォームが閉じないのか・・・。それに関して色々と調べてみましたが、解にたどり着けず、
わかる方いらっしゃいましたらご教授をお願いいたします。
代替案として、e.Cancel = false;の代わりにthis.Close()でフォームを閉じる処理を行わせると
再度App_FormClosing()が呼ばれますが、一度this.Close()を実行したら適当なフラグを立てておき
再度App_FormClosing()呼ばれた際にそのフラグが立っているとe.Cancel = false;のみ実行する、
という処理をすればフォームは閉じます。
###補足情報(言語/FW/ツール等のバージョンなど)
C#,VisualStudio2015
回答1件
あなたの回答
tips
プレビュー
バッドをするには、ログインかつ
こちらの条件を満たす必要があります。
2016/08/05 04:17
2016/08/05 04:33
2016/08/05 04:46
2016/08/05 05:02
2016/08/05 05:16
2016/08/05 06:20
2016/08/05 08:30