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

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

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

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

Unity3D

Unity3Dは、ゲームや対話式の3Dアプリケーション、トレーニングシュミレーション、そして医学的・建築学的な技術を可視化する、商業用の開発プラットフォームです。

Unity

Unityは、Unity Technologiesが開発・販売している、IDEを内蔵するゲームエンジンです。主にC#を用いたプログラミングでコンテンツの開発が可能です。

Q&A

解決済

2回答

1841閲覧

【Unity C#】Taskが吐くエラーがキャッチできない

super_hogehoge

総合スコア29

C#

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

Unity3D

Unity3Dは、ゲームや対話式の3Dアプリケーション、トレーニングシュミレーション、そして医学的・建築学的な技術を可視化する、商業用の開発プラットフォームです。

Unity

Unityは、Unity Technologiesが開発・販売している、IDEを内蔵するゲームエンジンです。主にC#を用いたプログラミングでコンテンツの開発が可能です。

0グッド

0クリップ

投稿2018/06/11 11:42

編集2018/06/12 05:03

前提・実現したいこと

いつも勉強させていただいております。 下記の問題がどうしても理解できません。
ご教授のほどよろしくお願い致します。

Unity 2017.1 上でC#で作業しております。

新規シーンで、Emptyオブジェクトに下記のスクリプトをコンポーネントとして
登録していただければ動作します。

Editor上での動作を想定しており、Editor実行時にConsole画面に"loop!"という
文字列が一気にwhile文で表示されSpaceキーを押すと止まります。

Taskを無限ループさせ、必要がなくなったらキャンセルできるような処理を実現したいのですが
Delayを入れてみたところ挙動が変わってしまいその理由が分からず困惑しています。

発生している問題・エラーメッセージ

ソースコード上の 「■■■■■■■■■■■■■■■■■■■■here■■■■■■■■■■■■■■■■■■■■」の2箇所の行のコメントアウトを外すと、 「□□□□□□□□□□□□□□□□□□question□□□□□□□□□□□□□□□□□□」のDebug.Logの文字「catch exception!」 が表示されなくなります。エラーがキャッチできていないようなのですが、 1.なぜエラーがキャッチできないのか 2.なぜ2箇所のコメントアウトを外しただけで処理が変わってしまうのか 3.エラーがキャッチできていないのになぜループ自体は止まるのか が理解できません。 具体的には、上記2箇所のコメントアウトがあると loop! loop! loop! catch exception! fin! 無いと loop! loop! loop! fin! となります。

該当のソースコード

C#

1using System; 2using UnityEngine; 3using System.Threading; 4using System.Threading.Tasks; 5 6public class NewBehaviourScript : MonoBehaviour { 7 8 CancellationTokenSource _tokenSource; 9 CancellationToken _token; 10 Task _cancel_key; 11 Action temp; 12 bool flag = true; 13 14 void Start () 15 { 16 _tokenSource = new CancellationTokenSource(); 17 _token = _tokenSource.Token; 18 temp = SampleFunc(); 19 20 _cancel_key = Task.Run(temp, _token); 21 22 } 23 24 private void Update() 25 { 26 if (Input.GetKey(KeyCode.Space)) 27 { 28 //複数回click防止フラグ 29 if (flag) 30 { 31 Cancel(); 32 flag = false; 33 } 34 } 35 } 36 37 public void Cancel() 38 { 39 _tokenSource.Cancel(); 40 try 41 { 42 _cancel_key.Wait(); 43 } 44 catch 45 { 46 Debug.Log("catch exception!"); //□□□□□□□□□□□□□□□□□□question□□□□□□□□□□□□□□□□□□ 47 } 48 49 _tokenSource.Dispose(); 50 Debug.Log("fin"); 51 } 52 53 54 private Action SampleFunc() 55 { 56 Action action = /*async*/ () =>  //■■■■■■■■■■■■■■■■■■■■here■■■■■■■■■■■■■■■■■■■■ 57 { 58 while (true) 59 { 60 Debug.Log("loop!"); 61 //await Task.Delay(1000); //■■■■■■■■■■■■■■■■■■■■here■■■■■■■■■■■■■■■■■■■■ 62 63 _token.ThrowIfCancellationRequested(); 64 65 } 66 }; 67 return action; 68 } 69}

補足情報(FW/ツールのバージョンなど)

C# .Net4.6
Unity 2017.1.0f3

新規シーンで、Emptyオブジェクトに上記のスクリプトをコンポーネントとして
登録していただければ動作します。

Editor上での動作を想定しており、Editor実行時にConsole画面に"loop!"という
文字列が一気にwhile文で表示されSpaceキーを押すと止まります。

UnityというよりはC#の書き方の問題だと思います。
私のC#のTask周りの知識が怪しく分かりにくいサンプルで申し訳ありません。

おそらく根本的な箇所を勘違いしてしまっていると思いますのでご指摘いただけるとありがたいです。

よろしくお願い致します。

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

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

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

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

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

guest

回答2

0

ベストアンサー

こんにちは。

この問題の直接的な原因は、temp関数がActionで宣言されていることです。
tempに非同期処理(コメントアウトを外した状態)を入れるということは、戻り値のない非同期処理、即ち、async void(待機不可能な非同期処理)を宣言していることと同意です。
コメントアウトを外した状態では、「キャンセルが発行されたあと、1秒以内に例外が出るが、その例外が出るのを"待たずに"キャンセル処理が先に進む」という状況になっています。
さらには、その例外はcatch可能なコンテキストでthrowされていないので、根本的にcatchできない状態になっています。

最低限の変更で意図した動作を実現するには、temp関数のActionFunc<Task>型に変更します。
Task.Runは真っ当な非同期メソッドを受け取った場合は適切に待機処理を受け渡してくれます。

投稿2018/06/11 23:17

tamoto

総合スコア4105

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

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

super_hogehoge

2018/06/12 05:06

御回答ありがとうございます。 なぜその問題が起こっているのか、その問題を修正するにはどうすれば良いのか を解説して下さった tamotoさんをベストアンサーにさせていただきました。 迅速にご回答くださり非常に助かりました。 教えていただいた内容を元にきちんと仕様を再確認してみようと思います。 ありがとうございました。
guest

0

C#は基本的に非同期処理中の例外はキャッチできないという制約があります

1.なぜエラーがキャッチできないのか
→非同期処理中は例外をキャッチできない為です

2.なぜ2箇所のコメントアウトを外しただけで処理が変わってしまうのか
→async / await は非同期処理を示しているためです(逆にコメントアウトするとそれは同期処理ということになります)

3.エラーがキャッチできていないのになぜループ自体は止まるのか
→例外自体は発生しているため途中で止まっています(キャッチは出来ないですが)

追記:通常のC#においてasync / await での例外キャッチはReleaseビルドでは正常にキャッチ可能のようですUnityでは確認してないのでわからないですが…

投稿2018/06/11 19:09

編集2018/06/11 19:14
MMashiro

総合スコア2378

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

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

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

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

ただいまの回答率
85.48%

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

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

質問する

関連した質問