C# と VB.NET の質問掲示板

ASP.NET、C++/CLI、Java 何でもどうぞ

C# と VB.NET の入門サイト

ロード中に表示するフォームを別スレッドで

[トピック内 7 記事 (1 - 7 表示)]  << 0 >>

■87549 / inTopicNo.1)  ロード中に表示するフォームを別スレッドで
  
□投稿者/ MTK (66回)-(2018/06/05(Tue) 11:22:32)

分類:[C#] 

お世話になります。

現在フォームが10個以上作成してあり、フォーム上のボタンを押すと必要なデータをロードして次のフォームが開くようになっています。
必要なデータをロードする時間が結構かかるため、ロード中に「ロード中」のような画面をフォームで前面出したいです。

そこで「ロード中」フォームを作って、データをロードしている間は別スレッドでそのフォームを起動させようと試みています。
最初に「ロード中」フォームを初期化して別スレッドで表示はできるのですが、その後に別のフォームからアクセスしようとすると
InvalidAsynchronousStateException「メソッドの呼び出し中にエラーが発生しました。  あて先のスレッドは存在しません」とエラーになります。


以下、プログラム ※LoadScreenFormというのが「ロード中」フォームのことです。


FormManager.cs
--------------------------------------------------

// ロード画面用フォーム
public static LoadScreenForm loadForm;

// Invokeで呼び出すためのdelegate 
private delegate void loadFormDelegateString(string txt);

// Invokeで呼び出したい関数を登録する
private loadFormDelegateString test;


public FormManager(string[] args)
{
	// ロード画面初期化、表示 ※これは成功します
	this.InitLoadFormAsync();

}

public async void InitLoadFormAsync()
{
	await Task.Run(() =>
	{
		// ロード中フォーム
		loadForm = new LoadScreenForm();

		loadForm.Show();
	});

	this.test = new loadFormDelegateString(loadForm.SetLoadText);

	loadForm.Invoke(this.test, "ログイン中...");
}


public void SetLoadText(string text)
{
	// ロード中のフォームに指定した文字を表示させる
	loadForm.Invoke(this.test, text);
}

--------------------------------------------------



TopMenuForm.cs   ※「this.bugyoMgr」には上記FormManager.csのインスタンスが入っています
--------------------------------------------------

private void OnLoad(object sender, EventArgs e)
{
	this.formMgr.SetLoadText("会社データロード中...");  // ここでエラー
}

--------------------------------------------------


以上、分かりにくかったら申し訳ないですが、教えて頂けないでしょうか。

引用返信 編集キー/
■87550 / inTopicNo.2)  Re[1]: ロード中に表示するフォームを別スレッドで
□投稿者/ とっちゃん (493回)-(2018/06/05(Tue) 11:39:42)
No87549 (MTK さん) に返信
>
> 以上、分かりにくかったら申し訳ないですが、教えて頂けないでしょうか。

何が聞きたいのでしょう?


> InvalidAsynchronousStateException「メソッドの呼び出し中にエラーが発生しました。 あて先のスレッドは存在しません」とエラーになります。

が発生している理由?
それとも発生場所?
もしくはそれ以外?

発生場所についてはデバッグ実行してみてください。
それで発生場所は特定できるはずです。

例外発生時に止まるようになっていない場合は止まるように設定してみてください。


引用返信 編集キー/
■87551 / inTopicNo.3)  Re[2]: ロード中に表示するフォームを別スレッドで
□投稿者/ MTK (67回)-(2018/06/05(Tue) 11:50:11)
No87550 (とっちゃん さん) に返信

回答ありがとうございます。
質問が分かりづらく申し訳ないです。


> 何が聞きたいのでしょう?
・ロード中フォームを別スレッドで出して、その後にアクセスする方法
・エラーが発生している原因と解決策
です。

発生場所については
TopMenuForm.cs の
> this.formMgr.SetLoadText("会社データロード中..."); // ここでエラー

というところまでは分かっています。
引用返信 編集キー/
■87552 / inTopicNo.4)  Re[3]: ロード中に表示するフォームを別スレッドで
□投稿者/ とっちゃん (494回)-(2018/06/05(Tue) 12:08:56)
No87551 (MTK さん) に返信
> ・ロード中フォームを別スレッドで出して、その後にアクセスする方法
Invoke() を使えばOKです。
この部分については問題ありません。

> ・エラーが発生している原因と解決策
Task.Run() で作成部分だけスレッドアウトした形にしていますよね?
フォームを作成したらタスクが終了してしまうため、
そのフォームを作ったスレッドがどこかに行ってしまいます。

また、Taskで割り当てされるスレッドは、MTAモードなので、フォーム作成を行うのには向きません。


> です。
> 
> 発生場所については
> TopMenuForm.cs の
>>this.formMgr.SetLoadText("会社データロード中...");  // ここでエラー
> 
> というところまでは分かっています。

この部分は、await から戻ってきてから動く部分ですよね?

フォームは、STAで動かす必要があり、なおかつそのSTAは、UIスレッドである必要があります。
詳細は省きますが、UIスレッドを自力で起こせない場合は、メインスレッド(メインのフォームを作成しているスレッド)で
フォームを作り、時間のかかる処理を別スレッドにするほうが構造上は安定します。

今回のようなプログラムであれば

public  void InitLoadFormAsync()
{
	loadForm = new LoadScreenForm();
	loadForm.Show();
}
private async void OnLoad(object sender, EventArgs e)
{
	SetLoadText( "会社データロード中..." );	//	中身はInvokeではなく直接セットに変える(実装がぐちゃぐちゃで実現できないので省略)
	await Task.Run( ()=> データロードの時間のかかる処理() );
	//	処理終了後に何かやりたいなら、ここでやる(ログイン中...に変える?)
}

という感じにすれば、安定すると思いますよ。
書きなぐりコードなので、自分の都合に合わせて直してくださいね。

引用返信 編集キー/
■87555 / inTopicNo.5)  Re[4]: ロード中に表示するフォームを別スレッドで
□投稿者/ MTK (68回)-(2018/06/05(Tue) 15:30:30)
No87552 (とっちゃん さん) に返信

>>・エラーが発生している原因と解決策
> Task.Run() で作成部分だけスレッドアウトした形にしていますよね?
> フォームを作成したらタスクが終了してしまうため、
> そのフォームを作ったスレッドがどこかに行ってしまいます。
>
> また、Taskで割り当てされるスレッドは、MTAモードなので、フォーム作成を行うのには向きません。

なるほど!そういうことだったんですね。
MTAとSTAのことは知りませんでした。勉強になりました。
エントリポイントで[STAThread]を宣言していてもTaskで実行するとMTAモードになるんですかね
スレッドがどこかに行ってしまうのであれば、ロード中フォームのスレッドを維持してやればいいんでしょうか?


>>発生場所については
>>TopMenuForm.cs の
> >>this.formMgr.SetLoadText("会社データロード中..."); // ここでエラー
>>
>>というところまでは分かっています。
>
> この部分は、await から戻ってきてから動く部分ですよね?

はい、この部分は戻ってきてから動く部分です。



> フォームは、STAで動かす必要があり、なおかつそのSTAは、UIスレッドである必要があります。

フォームはSTAでかつUIスレッドである必要がある というのは、
フォームはメインスレッドで呼び出す必要があり、Task.Run のような別スレッドで呼び出してはいけないという理解で正しいでしょうか?



> 詳細は省きますが、UIスレッドを自力で起こせない場合は、

すいません、ここの部分は自分でも調べてみたのですが、まだ理解できていません。
UIスレッドを自力で起こせない場合 というのが理解できていないのですが、どういう意味でしょうか?



> メインスレッド(メインのフォームを作成しているスレッド)でフォームを作り、時間のかかる処理を別スレッドにするほうが構造上は安定します。

今回の例の場合はどうするべきでしょうか?
メインスレッドで Aフォームを表示した際に、ロード中フォームを別で前面に出したいのです。
Aフォームのロード処理を別スレッドにしたとしても、
Aフォームもロード中フォームもメインスレッドで表示しなければいけないんですよね?



> 今回のようなプログラムであれば
>
> public void InitLoadFormAsync()
> {
> loadForm = new LoadScreenForm();
> loadForm.Show();
> }
> private async void OnLoad(object sender, EventArgs e)
> {
> SetLoadText( "会社データロード中..." ); // 中身はInvokeではなく直接セットに変える(実装がぐちゃぐちゃで実現できないので省略)
> await Task.Run( ()=> データロードの時間のかかる処理() );
> // 処理終了後に何かやりたいなら、ここでやる(ログイン中...に変える?)
> }
>
> という感じにすれば、安定すると思いますよ。
> 書きなぐりコードなので、自分の都合に合わせて直してくださいね。


プログラムまでいただいてありがとうございます。
調べて内容をきちんと理解できるようにしたいと思います。
引用返信 編集キー/
■87557 / inTopicNo.6)  Re[5]: ロード中に表示するフォームを別スレッドで
□投稿者/ とっちゃん (495回)-(2018/06/05(Tue) 16:28:25)
No87555 (MTK さん) に返信

> エントリポイントで[STAThread]を宣言していてもTaskで実行するとMTAモードになるんですかね

Taskの中身は別のスレッドで動きます。
Taskで割り当てられるスレッド(ワーカースレッド)は、MTAで動作しているのでSTAにできません。


> スレッドがどこかに行ってしまうのであれば、ロード中フォームのスレッドを維持してやればいいんでしょうか?
平たく言えばそうなりますが、もしそうするなら、Taskではなく、Threadクラスを使って
長時間維持を前提としたスレッドにする必要があります(メソッド抜けたらなくなったではだめなので)。


> フォームはSTAでかつUIスレッドである必要がある というのは、
> フォームはメインスレッドで呼び出す必要があり、Task.Run のような別スレッドで呼び出してはいけないという理解で正しいでしょうか?
>
後述していますが、UIスレッドとなるSTAスレッドで動かせばよいとなります(UWPアプリを除く)。

>>詳細は省きますが、UIスレッドを自力で起こせない場合は、
>
> すいません、ここの部分は自分でも調べてみたのですが、まだ理解できていません。
> UIスレッドを自力で起こせない場合 というのが理解できていないのですが、どういう意味でしょうか?


UIスレッドは、ウィンドウ(UIオブジェクト)を作成してよいスレッドになります。
UIオブジェクトは、Windowsメッセージを処理するスレッドでのみ作成ができます。

Windowsメッセージを処理するとは、メッセージポンプがあるなどの言い方もしますが
Windows Forms アプリの場合は、Application.Run を呼び出したスレッドを指します。
(WPFの場合は、Dispatcher.Run(), UWPの場合はUIスレッドを自分で作ることはできない)

UIスレッドを自力で起こすというのは、具体的には、
1.Threadクラスを使って、カーネルスレッドを起動する。
2.内部で、Application.Run() を呼び出してメッセージポンプを稼働する。
3.適切な処理で終了するように作りこむ。

という3つを合わせた実装を指します。
具体的にどうすればいいか?はアプリケーションによるので、実例はあまりありません。



> 今回の例の場合はどうするべきでしょうか?

先に乗せたプログラムのようにすればいいと思います。

引用返信 編集キー/
■87559 / inTopicNo.7)  Re[6]: ロード中に表示するフォームを別スレッドで
□投稿者/ MTK (69回)-(2018/06/05(Tue) 18:46:44)
No87557 (とっちゃん さん) に返信

長々とお付き合い頂きありがとうございました。

まだ分からない部分はありますが、取っかかりを頂けたので自分でももう少し調べてみたいと思います。
解決済み
引用返信 編集キー/

このトピックをツリーで一括表示


トピック内ページ移動 / << 0 >>

このトピックに書きこむ