スレッド起動したフォームを正常に終了させる方法

このQ&Aのポイント
  • Visual Studio 2005 C#.netを使用して、クラスからフォームをバックグラウンドスレッドで起動し、正常に終了させる方法について教えてください。
  • 現在、スレッドをAbortで強制終了させているため、Dispose()が正しく行われていない問題があります。呼び出し側からアクションを起こし、スレッド側からフォームを正常に終了させる方法を知りたいです。
  • また、Showメソッドを使用するとフォームの表示が一瞬で終わってしまう問題があります。この理由も教えてください。
回答を見る
  • ベストアンサー

スレッド起動したフォームを正常に終了させる。

Visual Studio 2005 C#.netです。 クラスからフォームをバックグラウンドスレッド起動して、そのフォームを起動したクラスから正常にフォームを消去させたい(終了)させたいのですが、どうすればいいのでしょうか? 現在、以下のプログラムにて実現していますが、Abortで強制終了させているのでDispose(); も行われていないと思います。 呼び出し側からアクションを起こしてAbortではなく、スレッド側から正常に終了させたいのです。 また、Showメソッドだとフォーム表示が一瞬で終わってしまうのは何故でしょうか? よろしくお願いいたします。 クラス: public class FormDisplayClass { //スレッド private System.Threading.Thread thread; //フォーム private TestForm TF = new TestForm();//フォームは別に存在するとします。 //表示メソッド public void Show() { //スレッド設定 this.thread = new System.Threading.Thread( new System.Threading.ThreadStart(FormShow)); //バックグラウンド this.thread.IsBackground = true; //スタート this.thread.Start(); } /// <summary> /// スレッドを終了させる。 /// </summary> public void Close() { this.thread.Abort(); } /// <summary> /// フォームを表示させる /// </summary> private void FormShow() { this.SWD.ShowDialog();//表示      (Showだと表示を持続できない) this.SWD.Dispose(); } #endregion } 呼び出し側: FormDisplayClass FDC = new FormDisplayClass();    private 表示メソッド { FDC.Show(); }    private 終了メソッド { sw.Close(); } } よろしくお願いします。

質問者が選んだベストアンサー

  • ベストアンサー
  • redfox63
  • ベストアンサー率71% (1325/1856)
回答No.1

FormDisplayClassにスレッドの実行関数を用意します またフォームが開いているかどうかを表すフラグも用意します // フォームを閉じるためのフラグ private Bool bFlag; // スレッド実行関数 protected void myProc() {   // フォームを表示   this.TF.Show();   // フォームが破棄されるまでループを回る   while ( !this.TF.IsDisposed )   {     // 他のスレッド止めないための処理     Application.DoEvents();     // 破棄される前に 閉じろフラグが立った時     if ( bFlag && !this.TF.IsDisposed )     {       this.TF.Close();     }   } } public void show() {   // フォームが破棄されている場合   if ( !this.TF.IsDisposed )   {     this.TF = new TestForm();   }   bFlag= false;   //スレッド設定   this.thread = new System.Threading.Thread( new System.Threading.ThreadStart(myProc));   //バックグラウンド   this.thread.IsBackground = true;   //スタート   this.thread.Start(); } public void Close() {   // フラグをセットするだけにする   bFlag = true;   // 次回のmyProc内のループでフラグがチェックされてフォームが閉じる   // フォームが閉じるとwhileループを抜け myProcも終了   // この時点でthreadが終了する } といった具合でしょう ・・・

cinquecent
質問者

お礼

早速のアドバイスありがとうございます。 うまく行きました。 ありがとうございました。 このフラグ&ループのリソース使用については、 負担がかからないものなのでしょうか。

その他の回答 (5)

  • redfox63
  • ベストアンサー率71% (1325/1856)
回答No.6

> DoEventsをWhileでループさせる場合、Sleepを入れないとビジー状態になるという件ですが、 > そのままにしておくとどのような弊害が起こるのでしょうか? 少々勘違いがあるようです Sleepを入れる、入れないではなくて DoEventsを入れるかどうかなのです DoEventsを入れずにwhileループをまわしても TestForm側のイベント処理等が何もされなくなってしまうという現象が起きます これはスレッドが実行すべき項目が whileループの中だけにとどまってしまうためです SleepやWaitOneなどで他の処理へCPUの力を振り分ける機会を多くしてやればいいかと思いますよ

cinquecent
質問者

お礼

解説有難うございます。 少しずつ理解出来てきたと思います。 とりあえずは、フラグ&ループの形式で目的は達しているので、 これで行きたいと思います。 色々ご親切にありがとうございました。 また、何かの時はよろしくお願いいたします。

  • redfox63
  • ベストアンサー率71% (1325/1856)
回答No.5

メインスレッド側で何かのプロシージャやイベントの中で ビジーループ(大きなループ処理でメッセージポンプを止めてしまうようなもの)があるとカクカクした動きはSleepを小さくしても直らないかもしれません AutoResetEventについては クラスのメンバーとして宣言しておいて スレッドのメインループの中でSleepを使用している部分と置き換えます class FomDisp { private AutoResetEvent myEvent; private Thread thread; private TestForm TF; // コンストラクタ Public FormDisp() { thread = new Thread( new ThreadStart( myProc ) ); myEvent = new AutoResetEvent( false ); } // スレッドのメインループ private void myProc() {   TF.Show();   // イベントがセットされるまで 100ms待機する   while ( !myEvent.WaitOne( 100, false ) ) {     Application.DoEvents();   }   TF.Close(); } public void show() {   TF = new TestForm();   thread.Start(); } publicvoid close() {   // メインループを終了するためにセットする   myEvent.Set(); } } といった具合です ・・・

cinquecent
質問者

お礼

アドバイス有難うございます。 AutoResetEventsを試してみたところ、 動作はしましたが、ご指摘の通り、動作が遅い状況となりました。 一つ教えて頂きたいのですが、 DoEventsをWhileでループさせる場合、 Sleepを入れないとビジー状態になるという件ですが、 そのままにしておくとどのような弊害が起こるのでしょうか? また、5mmSec程度のsleepをループに噛ます事でこの弊害を 回避する事が出来ていると言えるのでしょうか? 大変恐縮ですが、よろしくお願いいたします。

  • redfox63
  • ベストアンサー率71% (1325/1856)
回答No.4

スプラッシュウィンドウを表示させたいのであれば そのアプリのメインフォームのコンストラクタでスプラッシュウィンドウ用のフォームをインスタンス化してShowしてしまえばいいように思います ・・・ で、メインフォームのShownイベントでそのスプラッシュをCloseすればいいのでは ・・・ と感じますがいかがなのでしょう カクカクした動きになるのは Sleepの具合だと思いますので 500を小さくしてみましょう または Thread.Sleepで待つのではなくてAutoResetEventのWaitOneとかで待つようにするとかでしょうか メインフォーム側のループの中に 100毎とか500毎などに Application.DoEvents() を実行してみるとか ・・・

cinquecent
質問者

お礼

お返事が遅くなって申し訳ありません。 AutoResetEventについて調べいるのですが、 どこにどう挿入すれば良いのかを理解するのにはもう少し時間がかかりそうです。 とりあえず、Sleepの値を小さくしてなんとか目的は果たせそうですので、 これで凌いでいこうと思います。 有難うございました。

  • redfox63
  • ベストアンサー率71% (1325/1856)
回答No.3

何をどのようにしたいのかの情報がわれわれ回答者にはまったくありません スレッド仕立てにしてFormを管理ってのはあんまりしないと思います 逆に Formの中にスレッドをこしらえて このスレッドから来るイベントやメッセージによりFormの描画を変更する といったことはよくやる手ですよ 2005以降なら BackgroundWorkerなどがありますし ・・・

cinquecent
質問者

補足

アドバイス有難うございます。 情報が欠落していました。失礼しました。 目的の動作ですが、 起動に時間のかかる同C#アプリケーションがあり、そのアプリケーションが起動するまでの間(shownイベントまで) 「起動中です」という、例えばウインドウズが起動中に出すような動きのある画面を出しておきたいのです。 ただ、この機能は他にも転用可能としてクラス化し、当然、呼び出す側はインスタンス、 show,close等の簡単な記述で完了させたいと言う事が目的です。 今回は、この表示(form)をクラスからThreadで起動し、表示をしようと考えておりました。 目的を記述するのはマナー違反かと思い自粛していた次第です。 何か、アドバイスありましたらよろしくお願いいたします。

  • redfox63
  • ベストアンサー率71% (1325/1856)
回答No.2

スレッド実行ループがビジーループに近い状態ですのでちょっとまずいですね protected void myProc() {   // フォームを表示   this.TF.Show();   // フォームが破棄されるまでループを回る   while ( !this.TF.IsDisposed )   {     // 他のスレッド止めないための処理     Application.DoEvents();     // 破棄される前に 閉じろフラグが立った時     if ( bFlag && !this.TF.IsDisposed )     {       this.TF.Close();     }     // ここに追加     Thread.Sleep( 500 );   } } といった感じで Sleepを挿入してみてください

cinquecent
質問者

お礼

アドバイスありがとうございます。 sleepを挿入してみたのですが、 表示しているフォーム上に、 オブジェクトを動かす動作をしているのですが、 これがsleepの影響を受けてうまく動きません。 そもそも、Thread上でフォームの表示を行うのは イレギュラーなのでしょうか?

関連するQ&A

  • C# スレッド終了の監視について

    お世話になります。 C#2005でプログラムを作成しております。 マルチスレッドでの、スレッドの終了の監視のことでご質問させていただきます。 下記のコードを実行すると、問題なく実行されます。 ------------------------------------------------------------------------------------ private int SetDataTreeView() { Thread RcvSetThread = new Thread(new ThreadStart(this.RcvDataSet)); RcvSetThread.Start(); } delegate void RcvDataSetDelegate(); void RcvDataGridSet() { /* フォーム内のDatagidviewへの値の代入 */ } void RcvDataSet() { Invoke(new RcvDataSetDelegate(RcvDataGridSet)); } ------------------------------------------------------------------------------------ このスレッドの終了を監視したく、下記のコードを追加してデバッグしてみましたが、 スレッドが実行されませんでした。 ------------------------------------------------------------------------------------ int isRcvSearchFlg = 0; private int SetDataTreeView() { Thread RcvSetThread = new Thread(new ThreadStart(this.RcvDataSet)); RcvSetThread.Start(); // スレッドが終了するまで待機する for (; ; ) { if (isRcvSearchFlg == 1) { break; } } } delegate void RcvDataSetDelegate(); void RcvDataGridSet() { /* フォーム内のDatagidviewへの値の代入 */ isRcvSearchFlg = 1; } void RcvDataSet() { Invoke(new RcvDataSetDelegate(RcvDataGridSet)); } ------------------------------------------------------------------------------------ また、以下も試してみましたが、結果は同じでした。 ------------------------------------------------------------------------------------ private int SetDataTreeView() { Thread RcvSetThread = new Thread(new ThreadStart(this.RcvDataSet)); RcvSetThread.Start(); // スレッドが終了するまで待機する RcvSetThread.Join(); } delegate void RcvDataSetDelegate(); void RcvDataGridSet() { /* フォーム内のDatagidviewへの値の代入処理 */ } void RcvDataSet() { Invoke(new RcvDataSetDelegate(RcvDataGridSet)); } ------------------------------------------------------------------------------------ スレッドの終了を監視する方法がわからず困っております。 お手数ですが、ご教授いただきたくよろしくお願い申し上げます。

  • Thread.Abortメソッド後の処理について

    Thread.abortメソッドについて分からないことがあったので,助言が頂けると嬉しいです. Thread.Abort () を発生させると,通常そこでスレッドが終了するということなので,その後のスレッド内の処理はスルーされると思っていたのですが,下記コードですとその後の pictureBox1.Image = null;も実行されている様です. mainでスレッド作成→threadAの停止になると考えていたのですが,delegate内では反映されないのでしょうか? Thread thread_a; //スレッド delegate void Del(); private void main(object sender, EventArgs e) { //スレッドを開始 thread_a = new Thread(new ThreadStart(threadA)); thread_a.Start(); } private void threadA() { new Thread(new ThreadStart(delegate { Invoke((Del)delegate { thread_a.Abort(); //画像を消去する pictureBox1.Image = null; }); })).Start(); }

  • 別スレッドとイベントの終了手順について

    C#の質問になります。 メインフォーム上で別スレッドを起動し、別スレッドからのイベントで メインフォーム上のテキストボックスにメッセージを表示しています。 サンプルソースはフォームにボタン2個とテキストボックス1個を貼り 付けたものになり、ボタン1でスレッド起動、ボタン2で停止させてい ます。 正常パターンでボタン1とボタン2を交互に押下すると意図したとおり テキストボックスにメッセージが出力されます。 このプログラムで、ボタン1を押下し別スレッドが起動した状態で、 フォームの×ボタンを押下すると別スレッドの停止処理中にJoin() 呼び出しで永久に止まってしまいます。 止めるべきスレッド中でイベント(OnTraceEvent)を呼び出している のが問題のような気がします。(この処理がなければ正常) このような時の終了手順の王道的なものはありますでしょうか。   public partial class Form1 : Form   {     ThreadTest _thread = null;     public Form1()     {       InitializeComponent();     }     private void button1_Click( object sender, EventArgs e )     {       if ( this._thread == null )       {         this._thread = new ThreadTest();         this._thread.TraceEvent += new ThreadTest.TraceEventHandler( OnTrace );         this._thread.Open();       }     }     private void button2_Click( object sender, EventArgs e )     {       if ( this._thread != null )       {         this._thread.Close();         this._thread.TraceEvent -= new ThreadTest.TraceEventHandler( OnTrace );         this._thread = null;       }     }     private void OnTrace(String message)     {       if ( this.IsHandleCreated == false )       {         return;       }       MethodInvoker process = (MethodInvoker)delegate()       {         textBox1.AppendText( message + "\r\n" );       };       if ( this.InvokeRequired )       {         this.Invoke( process );       }       else       {         process.Invoke();       }       return;     }     private void Form1_FormClosed( object sender, FormClosedEventArgs e )     {       //フォームの×ボタンを押下した時にスレッドを停止しないと       //破棄されたコントロールを操作しようとするため下記を追加       if ( this._thread != null )       {         this._thread.Close();         this._thread.TraceEvent -= new ThreadTest.TraceEventHandler( OnTrace );         this._thread = null;       }     }   }   class ThreadTest   {     public delegate void TraceEventHandler( String message );     public event TraceEventHandler TraceEvent;     protected virtual void OnTraceEvent( String message )     {       TraceEventHandler TraceEventTemp = TraceEvent;       if ( TraceEventTemp != null )       {         TraceEventTemp( message );       }     }     private Thread _threadLoop = null;     private volatile Boolean _threadFlag = false;     public void Open()     {       //スレッド開始       if ( this._threadLoop == null )       {         this._threadLoop = new Thread( new ThreadStart( Loop ) );         this._threadLoop.Start();         while ( !this._threadLoop.IsAlive ) ;       }     }     public void Close()     {       //スレッド停止       if ( this._threadLoop != null )       {         this._threadFlag = false;         //this._threadLoop.Abort();//ここを有効にすればとりあえず終了する         this._threadLoop.Join();         this._threadLoop = null;       }     }     public void Loop()     {       this._threadFlag = true;       while ( this._threadFlag )       {         OnTraceEvent( DateTime.Now.ToString( "yyyy/MM/dd hh:mm:ss:fff" ) );         Thread.Sleep( 100 );       }     }   }

  • 2つのスレッドの実行について

    お世話になります。 VS2005C#で作成しております。 以下のコードにてスレッドを2つ実行させ、終了を監視する処理を記述しました。 ----------------------------------------------------------------------------------------------------------- isRcvSearchFlg = 0; isFwdSearchFlg = 0; private int SetDataTreeView() { // それぞれのデータ格納処理をスレッドで処理する Thread RcvSetThread = new Thread(new ThreadStart(this.RcvDataSet)); Thread FwdSetThread = new Thread(new ThreadStart(this.FwdDataSet));   RcvSetThread.Start(); FwdSetThread.Start(); // 両方のスレッドが終了するまで待機する while (isRcvSearchFlg != 1 && isFwdSearchFlg != 1) Application.DoEvents(); } delegate void RcvDataSetDelegate(); void RcvDataGridSet() { /* メインフォーム内のDatagidview(1)への値の代入 */ isRcvSearchFlg = 1; } void RcvDataSet() { Invoke(new RcvDataSetDelegate(RcvDataGridSet)); } delegate void FwdDataSetDelegate(); private void FwdDataGridSet() { /* メインフォーム内のDatagidview(2)への値の代入 */ isRcvSearchFlg = 1; } void FwdDataSet() { Invoke(new FwdDataSetDelegate(FwdDataGridSet)); } ----------------------------------------------------------------------------------------------------------- RcvDataGridSet、FwdDataGridSetでログを出力して、進行状況を監視しておりますが、どうも2つのスレッドが同時に走っていないようなのです。 原因として考えられることはございますでしょうか。 また、同時に走らせるコードはありますでしょうか。 お手数ですが、ご教授いただきたく宜しくお願い申し上げます。

  • Threadの終了

    http://sdc.sun.co.jp/java/docs/j2se/1.4/ja/docs/ja/api/index.html を見ると,スレッドの止め方が書いてありますが, これは,スレッドblinker内でループが回っていて初めてうまく行く, と(間違ってるかもしれませんが)理解しています. では下記のように,Thread2でループが回っていない場合は (*)の位置でThread2を止めるにはどうすればいいのか, 教えてくださる方はいらっしゃいませんでしょうか. (**)のClassAではSWTでGUIを表示させていて, それをThread1から閉じるというのが目的です. GUIを閉じない限りその先には進まないので, ループを回しThread2のrun()内で待機させることが出来なくて. public class Thread1 { Thread1(){ Thread2 t2 = new Thread2(); t2.start(); try { Thread.sleep(5000); } catch (InterruptedException e) { } (*)ここでThread2を終了させたい. } public static void main(String args[]){ new Thread1(); } class Thread2 extends Thread { public void run() { new ClassA(); ・・・(**) } } }

    • ベストアンサー
    • Java
  • スレッドが正しく終了されない

    C#を勉強して1ヶ月もたたないものです。 簡易ブラウザのフレーム?に座標を表示するものを作りたくて調べながら作ったんですが”ObjectDisposedExceptionはハンドルされませんでした”とでてデバックが正しく終了できません。 コードは public partial class Form1 : Form { public Form1() { InitializeComponent(); new Thread(new ThreadStart(GetMousePosition)).Start(); } private void Form1_Load(object sender, EventArgs e) { } public void GetMousePosition() { while (true) { SetText(); Thread.Sleep(100); } } public delegate void SetTextDelegate(); public void SetText() { if (InvokeRequired) { Invoke(new SetTextDelegate(SetText)); return; } } } です。解決策のヒントでもいいのでよろしくお願いします。 また、起動時間も表示したいんですが DateTime dt1 = DateTime.Now; DateTime dt2 = DateTime.Now; dt2-dt1 で表示できるみたいに書いてあったんですが時間がずっと0のままで進みません。 こちらのほうもご教授いただけると助かりますのでよろしくお願いします。

  • デリゲートで子から親フォームのメソッド実行について

    お世話になります。 .NET C#で開発をしております初心者です。 下記の様なデリゲートで子フォームから親フォームのメソッドを実行 させようとしています。 すると、parent2.MeisaiSyutoku(); の部分で エラーが発生してしまいます。 (エラー内容:NullReferenceExceptionはハンドルされませんでした。        オブジェクト参照がオブジェクト インスタンスに設定されていません。) 同じ子フォームで(親フォームは違いますが)、もう1つデリゲートをやっているのですが それも何か悪かったりするでしょうか。 下記のデリゲートで、デリゲート(2)はちゃんと実行されますが、デリゲート(1)の方でエラー となってしまいます。 どなたかご親切な方、違う方法でも構いませんので、子フォームから 親フォームのメソッドを実行する方法を教えて下さいます様、 宜しくお願い致します。      記 デリゲート(1) 子フォーム側 parent2; //親フォームの参照 public void setOya2(案内書 parent) { this.parent2 = parent; //親フォームから参照を受け取って保持する } private void button2_Click(object sender, EventArgs e) { ~イベント処理~ parent2.MeisaiSyutoku(); //案内書.csのメソッド実行 } 親フォーム側 private void button4_Click(object sender, EventArgs e) { Global.openMOTO = 1; 台帳 n台帳 = new 台帳(); n台帳.setOya2(this); n台帳.Show(); } private delegate void ctlFromChild(); public void MeisaiSyutoku() { new Thread(new ThreadStart(delegate { Invoke((ctlFromChild)delegate { Get_MeisaiSyutoku(); }); })).Start(); } デリゲート(2) 子フォーム側 送り状作成 parent1; //親フォームの参照 public void setOya1(送り状作成 parent) { this.parent1 = parent; //親フォームから参照を受け取って保持する } private void fpSpread1_CellDoubleClick(object sender, CellClickEventArgs e) { ~イベント処理~ parent1.changeTextBox4(zOtodokeSaki);         } 親フォーム側 private void button1_Click(object sender, EventArgs e) { Global.openMOTO = 0; 台帳 n台帳 = new 台帳(); n台帳.setOya1(this); n台帳.Show(); } private delegate void ctlFromChild(); public void changeTextBox4(string str) { new Thread(new ThreadStart(delegate { Invoke((ctlFromChild)delegate { this.textBox4.Text = str; }); })).Start(); }

  • C# フォームを閉じてもプログラムが終了しない

    前略 ・C#の初心者です。 ・下記のようなプログラムを作りたいと思っています。プログラムは<作りたいプログラム>の仕様どうりに動作していますが、Form1のFormClosingイベントに Application.Exit()を追加しないと フォーム1で "X"(閉じる)をクリックしても(フォームは非表示になりますが)プログラムが終了しません。プログラムでどこかおかしな部分があると思っています。Application.Exit()を追加しないでもプログラムを終了する方法を教えてください。 <作りたいプログラム> (1)Form1 のbutton1をクリックすると新しいForm2が作成され表示される。Form2が表示されるとForm1は非表示となる。 (2)Form2 のbutton1をクリックするとForm2が非表示となりForm1が表示される。 (3)Form1 の "X"(閉じる)をクリックしてプログラムを終了する。 //Form1.cs using System; using System.Collections.Generic; using System.ComponentModel; using System.Data; using System.Drawing; using System.Linq; using System.Text; using System.Windows.Forms; namespace formClose { public partial class Form1 : Form { public Form1() { InitializeComponent(); } private void button1_Click(object sender, EventArgs e) { Form2 form2 = new Form2(); form2.Show(); //フォーム2を表示 this.Hide(); //フォーム1を非表示 } private void Form1_FormClosing(object sender, FormClosingEventArgs e) { Application.Exit(); //アプリケーション終了 } } } //Form2.cs using System; using System.Collections.Generic; using System.ComponentModel; using System.Data; using System.Drawing; using System.Linq; using System.Text; using System.Windows.Forms; namespace formClose { public partial class Form2 : Form { Form1 form1 = new Form1(); public Form2() { InitializeComponent(); } private void button1_Click(object sender, EventArgs e) { this.Close(); //フォーム2を閉じる } private void Form2_FormClosing(object sender, FormClosingEventArgs e) { form1.Show(); //フォーム1を表示する } } } 以上

  • Excel2007 VBA シートとユーザーフォームの値のやり取り

    1.シートからユーザーフォームを呼び出す。 2.シートからユーザーフォームへ値を渡す。 3.ユーザーフォームからシートへ値を返す。 この一連の処理を行いたいのですが。 TestSheet----------- Dim oForm As TestForm Set oForm = New TestForm oForm.SetData("渡す値") Call oForm.Show Dim Result As String 'Result = oForm.GetResult Set、GetメソッドはTestForm内に存在します。 しかし、ユーザーフォームでUnloadを行った時点でoFormが破棄されるらしく、Result = oForm.GetResultでエラーとなります。 変数とSet、Getメソッドを標準モジュールに記述すれば解決するのですが、スコープ範囲がこのシートとユーザコントロールに限定できないのでなるべく記述したくありません。 他にユーザフォームから値を受け取る方法はありませんでしょうか。

  • C#2005のスレッド

    本スレッドとサブスレッドがあり、 サブそレッドが終了するまで本スレッドで待機する形をとっていますが、 (メソッドを実行後のreturn終了待ちなので) サブスレッドが終了するまでフォームが固まってしまいます。 でもサブスレッドでDoEventを起こすとエラーとなります。 さらにサブスレッド2で while(true) {Application.DoEvents();} を実行すればフォームは固まりませんが、 パフォーマンスが50%くらいになってしまいます。 サブスレッドで実行しつつ、 低パフォーマンスで、 フォームが動かせるようにするには どのような組み方をしたらいいのでしょうか?

専門家に質問してみよう