質問するログイン新規登録
C#

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

Q&A

解決済

5回答

17106閲覧

メソッド内で非同期処理を呼んで、その戻り値を取得したい。

hal_k

総合スコア18

C#

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

0グッド

2クリップ

投稿2018/08/01 07:18

編集2018/08/01 07:31

0

2

・メソッド内で非同期処理を呼んで、その戻り値を取得したい。

C#、.NetFramework4.0で開発しています。

ある処理の中で非同期処理を呼んで、その戻り値を取得したいです。
また、非同期処理が終了するまで呼び元の処理を待たせたいです。

非同期処理の中である値を取得するのですが、
その値を受け取った時点で非同期処理自体は終了し、
それが終わったことを呼び元の処理が判断できればと考えています。

非同期なのに、待つというのも変なのですが、
Thread.Sleepを使って待たせると、
呼び元が固まってしまうのを
回避したいため非同期処理を使用しています。

実際には下記の様な流れになります。
①メインフォーム→②あるクラスのメソッドを呼び出す。→③非同期処理を呼ぶ。

ご教授よろしくお願いいたします。

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

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

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

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

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

takabosoft

2018/08/01 07:30

固まってしまうのを 回避したいため、とありますが、固まらずに操作し放題でも良いのでしょうか?また、フレームワークはWinFormですか?
takabosoft

2018/08/01 07:32

もしかするとasync/awaitを使えば、解決するのかもしれません。何をしたいのかが見えないのでハズレかもしれませんが。
hal_k

2018/08/01 07:35 編集

takabosoft様 ご質問ありがとうございます。 フレームワークは.NetFramework4.0を使用しています。 async/awaitなのですが、これは4.5以降であれば使えるようでして、 今回は使えないのです。 何か代替の方法をご存知でしたらご教授いただけませんでしょうか?
guest

回答5

0

下記のサイトが参考になるかと。

非同期メソッド入門 (5) - 戻り値

投稿2018/08/01 07:56

PineMatsu

総合スコア3581

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

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

hal_k

2018/08/01 08:01

PineMatsu様 ご回答ありがとうございます。 ご提示いただいたサイトを早速拝見させていただきます。
hal_k

2018/08/01 08:07

PineMatsu様 ご提示いただいたサイトの「Task型」の部分が まさにやりたいことなのです。 ただ、残念ながら.NetFramework4では使えませんでした。 (async などが4.5以降で追加されているようです。)
PineMatsu

2018/08/08 07:51

それは残念です。.NetFrameworkのバージョンを上げられるのならこの方がスッキリするはずなのですが・・・。
guest

0

ベストアンサー

Task.Wait メソッド()で処理をウェイトしてあげると良いと思います
4.0で使えます。

参考元

使い方としてはざっくりと以下のような感じ

C#

1using System; 2using System.Threading.Tasks; 3 4public class Example 5{ 6 public static void Main() 7 { 8 //非同期処理をTaskでやる 9 Task t = Task.Run( () => { 10 Console.WriteLine("非同期処理だよ"); 11 } ); 12 //処理が終わるまでWaitのところでブロッキングする 13 t.Wait(); 14 } 15}

#追記(2018/08/04)
######フォームが固まる理由について
Waitを呼んだ処理がメインスレッドとして動いているのだと思います。

画面の描画やユーザーの操作を受け取ったりするのはメインスレッドだけです。
なので、もしメインスレッドをWaitしてしまった場合は画面が固まります。
メインスレッドでSleepもWaitもしないこと

######フォームが固まらないようにする方法
フォームが止まらないように直すのであれば、

ある変数の値が変わるまで待つ処理

この処理自体はメインスレッド以外のスレッドでやるべきです。

つまりメインスレッドは処理を続行し非同期処理用のスレッドを2つ作成する必要があります。

コードを添付します。
コメント文の「★」は後述の問題点です。

C#

1private void button1_Click(object sender, EventArgs e) 2{ 3 //ここでSleepしたらメインスレッドがSleepするので、画面が固まる 4 //Thread.Sleep(100);などしない 5 //ある変数の値が変わるまで待つ処理 6 Task taskForWait = Task.Run( () => { 7             Task t= Task.Run( () => { 8 //やりたい処理 9   } ); 10 11 t.Wait(); 12 //★どう通知するの?taskForWaitがtの終わりを知れればOK? 13 } ); 14 //★ここで値を受け取ったことを判断する処理を呼び出すと画面固まるので非同期処理の意味がない 15} 16

######上記コードの問題点
この実装だとメインスレッドはスレッドに処理を任せているだけなので
もし値を受け取ったことを判断する処理を呼び元(メインスレッド上)に作ってしまうと
非同期処理の意味がなくなってしまいます。

必ずしもタイマー処理で値を受け取ったことを判断する必要はあるのでしょうか。
例えば、値を受け取ったらイベントとして送ってあげて、
別で処理してあげる…とか色々方法はあると思います。

とにかく、現在の質問者様の意図を組み取る限りでは実装が難しいように思います。
何か誤解がありましたら教えてください。

投稿2018/08/02 07:33

編集2018/08/03 21:32
BluOxy

総合スコア2663

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

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

hal_k

2018/08/03 00:11

BluOxy様 ご回答ありがとうございます。 ご提示いただいたWaitメソッドを試してみました。 結果、非同期処理の完了を待たせることができました。 ただ、非同期処理中、ある変数の値が変わるまで待たせているのですが、 (具体的に言うとThread.Sleepです。) Waitをかけると、 呼び元(結果的にフォーム)が止まってしまいました。 Waitをかけなければ、呼び元が止まることはないです。 非同期処理を待つという事は、結局同期処理として扱っているのと 同じ気がしますので、 当然といえば当然な気もしますが。。。
BluOxy

2018/08/03 21:16 編集

回答を追記しました。
hal_k

2018/08/06 06:51

BluOxy様 丁寧なご回答ありがとうございます。 それにも関わらず返事が遅くなってしまい申し訳ありません。 おかげさまで呼び元(メインスレッド)上で止まってしまう理由について 把握することができました。 BluOxy様の提示にもありましたが、 非同期処理内で待たせる方法については見送ることにしました。 別の非同期処理を用意し、 そちらで値の変更があったことを監視するようにすることで、 解決できそうです。
guest

0

4.0 なので await は使えないとコメントされていたので。
開発環境が VS2012 以降であれば、ライブラリを参照すれば 4.0 でも await できますよ。
非同期メソッド入門 (12) - .NET Framework 4.5以外でawaitする

投稿2018/08/01 22:10

gaya-K

総合スコア449

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

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

hal_k

2018/08/02 00:38

gaya-K様 ご回答ありがとうございます。 返事が遅くなりすみません。 ライブラリの参照でawait使えるようになるのですね。 残念ながら環境はVS2010なのですが、 参考にさせて頂きます。
guest

0

Task処理は悩ましいもので、先日もしっかり、固まりました。

以下の方法はどうでしょうか?

Taskを用いた並列処理

Task t1 = new Task(Exec1);
t1.Start();

実際の実行メソッド (Exec1) の最後に、自分自身のメソッドを呼び出す。
ただし、BeginInvoke()を使って。

これならば、.NetFramework4.0 でもOKだと思います。

Timer処理ならば、Timerを Killする(Dispose?) もありかと。

投稿2018/08/01 11:29

pepperleaf

総合スコア6385

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

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

hal_k

2018/08/02 00:40

pepperleaf様 ご回答ありがとうございます。 返事が遅くなりすみません。 Taskでの並列処理を使うのですね。 ご提示いただいた方法、サイトともに参考にさせていただきます。
guest

0

非同期処理の終了時に実行するデリゲートを設定して、それでフォームに対してなにかをするとか
あるいはその待つ処理そのものを非同期処理にしてしまうとか

投稿2018/08/01 07:23

y_waiwai

総合スコア88180

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

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

hal_k

2018/08/01 07:32

y_waiwai様 早速のご回答ありがとうございます。 ところで、こちらの表現の仕方が紛らわしい箇所がありました。 「呼び元(メインフォーム)が固まってしまう」と表記しておりましたが、 実際の呼び元はメインフォームではありません。 実際には下記の様な流れになります。 ①メインフォーム→②あるクラスのメソッドを呼び出す。→③非同期処理を呼ぶ。 質問の方も修正しております。 申し訳ありません。 例えば、 ご提示いただいた「非同期処理の終了時に実行するデリゲートを設定」 という方法で、戻り値を呼び元のメソッドに返す事、 あるいは非同期処理が終了したことを通知できるでしょうか?
y_waiwai

2018/08/01 07:35

非同期処理の結果を返すためのデリゲート/コールバックですんで、その通りの使い方ですねー
hal_k

2018/08/01 07:42

y_waiwai様 ご回答ありがとうございます。 例えばどのように作成すると、 ご提示いただいた「デリゲート/コールバック」で 結果を取得できるかなどのサンプルをお教えいただけないでしょうか? あるいは参考になるようなサイトを教えていただけるだけでも構いません。 よろしくお願いします。
y_waiwai

2018/08/01 07:59

その非同期処理とはどうやって呼び出してるんでしょうか。 C#ではいろんな方法があるので、どれを使ってるかわからないと答えようがないです
hal_k

2018/08/01 08:35 編集

y_waiwai様 情報が足りず申し訳ありません。 呼び元メソッド中に非同期処理を書いています。 非同期処理は 「System.Timers.Timer」 を使って行っています。 このTimer処理の中で、 1秒ごとに値をチェックし、 値を取得したらタイマー処理を終了するようにしています。 この timer処理が終わったら 呼び元メソッドを終了したいけれど、 待たずに終了しているというのが現状です。 この Timer を使った処理が適しているのかも まだ分からない状態ですが、 見よう見まねで作成した次第です。
y_waiwai

2018/08/01 08:43

その非同期処理を呼び出す処理を非同期にすればどうなんでしょ。 そうすればいくら待ち処理いれてもフォームは影響ないですわな
hal_k

2018/08/01 08:52

y_waiwai様 「非同期処理をよんでいるものを非同期に」 たしかに仰る通りです。 その場合も、非同期にした呼び元の処理が終わったことを通知したいです。 それはやはり最初に教えていただいた「デリゲート/コールバック」で 通知することになるのでしょうか?
hal_k

2018/08/01 10:08

y_waiwai様 参考サイトのご教授ありがとうございます。 じっくり読んでみようと思います。
guest

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

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

ただいまの回答率
85.30%

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

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

質問する

関連した質問