質問をすることでしか得られない、回答やアドバイスがある。

15分調べてもわからないことは、質問しよう!

新規登録して質問してみよう
ただいま回答率
85.37%
C#

C#はマルチパラダイムプログラミング言語の1つで、命令形・宣言型・関数型・ジェネリック型・コンポーネント指向・オブジェクティブ指向のプログラミング開発すべてに対応しています。

.NET Framework

.NET Framework は、Microsoft Windowsのオペレーティングシステムのために開発されたソフトウェア開発環境/実行環境です。多くのプログラミング言語をサポートしています。

Q&A

解決済

2回答

3351閲覧

【C#】メインスレッドとは別のスレッドで実行しているTaskのエラーハンドリングを非同期で行う事は出来ますか?

hasshy

総合スコア102

C#

C#はマルチパラダイムプログラミング言語の1つで、命令形・宣言型・関数型・ジェネリック型・コンポーネント指向・オブジェクティブ指向のプログラミング開発すべてに対応しています。

.NET Framework

.NET Framework は、Microsoft Windowsのオペレーティングシステムのために開発されたソフトウェア開発環境/実行環境です。多くのプログラミング言語をサポートしています。

0グッド

0クリップ

投稿2020/06/19 18:27

C#で常駐アプリを開発しています。

以前、常時監視処理を実装すると、右クリックのメニューが動かなくなる件でご相談しました。
その際は、コンポーネント内で右クリックメニューと常時監視処理(無限ループ)を実装していたため、
常時監視処理で止まってしまい、右クリックメニューの描画が出来ませんでした。

そのため、それぞれのスレッドを非同期処理にすることで解決出来ました。

【C#】【.net】常駐アプリ用のコンポーネントでツールに付与したイベントが動かない

その後、別スレッドで動作させる常時監視処理にエラーハンドリングの処理を導入しようと考えました。
エラーハンドリング自体は出来たのですが、以前と同じように右クリックメニューが動かなくなる状態になりました。

後述しますが、エラーハンドリングをするために実行しているスレッドが終わるまで待つ(Task.wait)処理を使っているからかもしれません。

別スレッドのエラーハンドリングを非同期で実施するにはどうしたらよいでしょうか?

概要

接続されたカードリーダーからカード情報を取得して、カード情報を処理したいと考えています。
常駐アプリとして実装して、右クリックメニューからアプリケーションを終了させられるようにします。

状況

右クリックメニュー(UI)と常時監視する処理は実装出来ました。
それぞれ別スレッドで稼働させることでどちらか片方が完了しない状態にはならなくなりました。

TaskのErrorを取得してハンドリングする事は出来ます。
公式の実装方法を参考に、Task.Waitで終了、もしくはエラーが出るまで待たせるようにしました。
例外処理 (タスク並列ライブラリ)

ただし、別スレッドの処理が実行されている間は、右クリックのメニューのスレッドが止まってしまいます(ローディングのアイコンが表示しっぱなし)。

cs

1public NotifyIconWrapper() 2{ 3 InitializeComponent(); 4 5 // 終了メニューのイベントリスナーを設定 6 this.toolStripMenuItemExit.Click += new EventHandler(this.ToolStripMenuItemExitClick); 7 8 // 省略 9 10 this.nfcReaderTask.Start(); 11 try 12 { 13 // エラーが発生するまで待つ 14 this.nfcReaderTask.Wait(); 15 } 16 catch (AggregateException ae) 17 { 18 // 省略 19 } 20} 21

また、エラーが発生してタスクが止まると右クリックメニューが選択できるようになります。

ソース

App.xaml.cs

タスクトレイのアイコンについては、常時監視の処理も含めてNotifyIconWrapperコンポーネントクラスを別途用意しました。
App.xaml.csでインスタンスを作成することでコンポーネントを有効にしています。

using Sample.Components; using System; using System.Windows; namespace Sample { /// <summary> /// App.xaml の相互作用ロジック /// </summary> public partial class App : Application { /// <summary> /// タスクトレイに表示するアイコン /// </summary> private NotifyIconWrapper notifyIcon = new NotifyIconWrapper(); }

NotifyIconWrapper コンポーネント

常駐アプリで行う、常時監視の処理を実装しています。
最初に記載しましたが、常時監視の処理をそのまま書くとUIの描画が止まるのでTaskを作り別スロットで処理しています。

cs

1using Sample.Classes.NfcReader; 2using System; 3using System.ComponentModel; 4using System.Diagnostics; 5using System.Threading.Tasks; 6using System.Windows; 7 8namespace Sample.Components 9{ 10 11 /// <summary> 12 /// タスクランナー用のアイコン 13 /// </summary> 14 public partial class NotifyIconWrapper : Component 15 { 16 17 private Task nfcReaderTask; 18 19 /// <summary> 20 /// コンストラクタ 21 /// </summary> 22 public NotifyIconWrapper() 23 { 24 InitializeComponent(); 25 26 // 終了メニューのイベントリスナーを設定 27 this.toolStripMenuItemExit.Click += new EventHandler(this.ToolStripMenuItemExitClick); 28 29 // カードリーダー用タスク追加 30 this.nfcReaderTask = new Task(() => 31 { 32 // カードリーダーのインスタン作成 33 NfcMain nfcMain = new NfcMain(); 34 while (true) 35 { 36 try 37 { 38 // ハンドルが初期状態以外であれば 39 if (nfcMain.context != IntPtr.Zero) continue; 40 // カード読み込み実行 41 nfcMain.start(); 42 } 43 catch 44 { 45 Debug.WriteLine("Task実行中にエラー発生"); 46 throw new AggregateException("カードリーダーに接続できませんでした。"); 47 } 48 } 49 }); 50 51 // 別スレッドでカードリーダーの監視処理を実行開始 52 this.nfcReaderTask.Start(); 53 try 54 { 55 // 元の処理が終わるか、エラーが発生するまで待つ 56 this.nfcReaderTask.Wait(); 57 } 58 catch (AggregateException ae) 59 { 60 // エラーが発生した場合はエラーメッセージを表示 61 MessageBox.Show( 62 ae.InnerException.Message, 63 "サンプルキャプション", 64 MessageBoxButton.OK, 65 MessageBoxImage.Hand 66 ); 67 68 foreach (var e in ae.InnerExceptions) 69 { 70 if (e is CustomException) 71 { 72 Console.WriteLine(e.Message); 73 } 74 else 75 { 76 throw; 77 } 78 } 79 } 80 } 81 82 /// <summary> 83 /// コンテキストメニュー、「終了」のイベント 84 /// </summary> 85 /// <param name="sender"></param> 86 /// <param name="e"></param> 87 private void ToolStripMenuItemExitClick(object sender, EventArgs e) 88 { 89 System.Windows.Application.Current.Shutdown(); 90 } 91 } 92 93 public class CustomException : Exception 94 { 95 public CustomException(String message) : base(message) 96 { 97 98 } 99 } 100}

気になる質問をクリップする

クリップした質問は、後からいつでもMYページで確認できます。

またクリップした質問に回答があった際、通知やメールを受け取ることができます。

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

dodox86

2020/06/19 23:46

前の質問でいただいた回答への誤解と、Taskの使い方に関する誤解もあわさっているのではないでしょうか。本質問でご提示のコードはNotifyIconWrapperのコンストラクタでTaskをで生成していますが、その中でStart()し、Wait()もしています。Appクラスのインスタンスの生成時にNotifyIconWrapperのインスタンスを生成しているので、ブロックして当然(つまり、今の状況)なのでは?
hasshy

2020/06/20 04:26

何時もご指摘いただきありがとうございます。 頂いた内容についても知識不足で理解が及ばず申し訳ありません。 Taskについてはドキュメントを確認します。 > Appクラスのインスタンスの生成時にNotifyIconWrapperのインスタンスを生成しているので、ブロックして当然(つまり、今の状況)なのでは? なるほどですね。 一連の流れを実行してしまっているので、 Waitでタスクを待つ処理は、インスタンスを作った後に別処理でしないといけなさそうですね。
YAmaGNZ

2020/06/20 05:31

待つ必要があるのですか?
hihijiji

2020/06/20 10:05

コンストラクタで例外を投げるのは初期化すると重いリソース持ってしまうのでそれを持ちたくない場合や、アプリケーションが原則続行不能で終了する時ぐらいです。 続行できる例外をつかんだら、例外を投げず戻り値やメッセージ、通信などで返してあげましょう。 要約するとむやみに例外を投げないようにしましょう。
YAmaGNZ

2020/06/21 00:42 編集

現状、Task内でtry~catchしているので、例外をハンドリングしているとも取れます。 Taskで例外が発生した時、ユーザーに通知しプログラムを終了するという処理にしたいのでしょうか? この為、UI側でTaskが例外にて終わるか監視する目的でWaitにて待つという処理としたということですかね?
hasshy

2020/06/21 01:47

理解が及ばずご返答が遅れてしまい申し訳ありません。 YAmaGNZ様 > Taskで例外が発生した時、ユーザーに通知しプログラムを終了するという処理にしたいのでしょうか? > この為、UI側でTaskが例外にて終わるか監視する目的でWaitにて待つという処理としたということですかね? 仰っている事にとても近いです。 最終的には、ユーザーに通知して、カードリーダの準備が出来たら新しくTaskを作りたいです。 まず、例外で操作できなくならないように、仰る通りユーザーに通知しプログラムを終了するところから実装している所でこの質問をいたしました。 説明が足りず失礼しました。
guest

回答2

0

ベストアンサー

「ユーザーに通知して、カードリーダの準備が出来たら新しくTaskを作りたい」ということであれば、カードリーダの取得~カードの認識読み込みまでをTaskとして、その中で例外が発生したら、Invoke等でダイアログを表示する処理にすれば、Taskを生成して例外を待つということを行わなくてもいいのではないですか?

C#

1Task.Run(() => 2{ 3 while(true) 4 { 5 //カードリーダの検出処理 6 if (検出した) 7 { 8 try 9 { 10 // カードリーダの処理ループ 11 } 12 catch (Exception) 13 { 14 // invoke等でMessageBox等でユーザーに通知 15 } 16 } 17 } 18} 19); 20

投稿2020/06/21 02:01

YAmaGNZ

総合スコア10469

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

hasshy

2020/06/25 17:48

ご連絡が遅れてしまい申し訳ありません。 ありがとうございました。 頂いたコードを参考に、メインスレッドを止めることなくサブスレッドでTaskを実行できるようになりました。 ご教示頂きありがとうございます。
guest

0

そのスレッド内でエラーハンドリングするだけの話だと思いますが、それでなにか不具合があるんでしょうか

投稿2020/06/19 21:37

y_waiwai

総合スコア88024

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

hasshy

2020/06/20 04:04 編集

度々ご回答いただきありがとうございます。 > そのスレッド内でエラーハンドリングするだけの話だと思いますが、それでなにか不具合があるんでしょうか タスクを再実行出来ないのではないでしょうか。 何らかのでエラーがタスクが止まってしまった場合、アプリケーションが止まってしまいユーザーに状況を伝えられないのではないかと考えています。 アプリケーションを復旧するために、タスクのエラーを取得する必要があると考えています。 もしかして、以前いただいたご回答のクラスに変更を加える処理というのがこれに該当するのでしょうか? > コンストラクタの途中では、そのクラスがまだ生成されてないってことなんで、タスク中にそのクラスをどーこ~する処理があるとまずいですが。
y_waiwai

2020/06/20 04:57

whileでループさせて、その中でtry-catchすればいいだけなんでは。 エラーが出たならメインスレッドに通知していけばいいだけの話で。 わざわざメインスレッドを待たせる必要はないです
hasshy

2020/06/25 17:53

ご回答いただきありがとうございます。 ご連絡が遅れてしまい失礼いたしました。 勉強不足だったので、一度ドキュメントを見返しておりました。 仰る通り、Waitでサブスレッドを待つ必要が無く、Task.Runの中でtry-catchを実装すればよいという事をやっと理解できました。 ご解説頂きありがとうございます。
guest

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

15分調べてもわからないことは
teratailで質問しよう!

ただいまの回答率
85.37%

質問をまとめることで
思考を整理して素早く解決

テンプレート機能で
簡単に質問をまとめる

質問する

関連した質問