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

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

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

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

Q&A

解決済

2回答

2403閲覧

C#で2つのTaskを同時実行かつGUIとは別スレッドで実行したい

退会済みユーザー

退会済みユーザー

総合スコア0

C#

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

0グッド

1クリップ

投稿2017/11/21 08:03

2つの株の板情報を取得するアプリケーションを作っています。
毎秒取得するので、2つのURLに同時アクセスして取得したいのですが、同時取得はできましたがGUIのフリーズが頻発してしまいます。

C#

1// 2つのタスクを作ってほぼ同時にアクセスします。 2// GetPriceLevel1()とGetPriceLevel2()はURLにアクセスして板情報を取得する関数です。 3Task<int> task1 = Task.Run(() => GetPriceLevel1()); 4Task<int> task2 = Task.Run(() => GetPriceLevel2()); 5 6// 2つの板情報が取得されるまで待ちます。 7Task.WaitAll(task1, task2); 8 9// 板情報が取得されたら出力します。 10Console.WrightLine("板1:" + task1.Result.ToString()); 11Console.WrightLine("板2:" + task2.Result.ToString());

ここまではうまくいきました。ほぼ同時に2つの板にアクセスして情報を取得できています。

このコードを関数化し、GUIのFormにSystem.Threading.Timerなどを使って毎秒取得するとGUIがフリーズしてしまいます。
必ずフリーズするのではなく、頻発する感じです。

いろいろ調べていると

同期メソッドから非同期メソッドを呼び出すとアプリケーションがフリーズする - 非同期メソッド呼び出しによるデッドロック

という記事を見つけました。
Task.RunにConfigureAwait(false)メソッドを指定するとGUIスレッドとは別のスレッドで処理してくれるそうです。
しかし、私のコードでは2つのタスクがあるので、

c#

1int task1 = await Task.Run(() => GetPriceLevel1()).ConfigureAwait(false); 2int task2 = await Task.Run(() => GetPriceLevel2()).ConfigureAwait(false); 3 4// Task.WaitAll(task1, task2); ←上記で2つawaitを指定しているのでこれは指定できない。

のようにしてしまうと同時にGetPriceLevel1()とGetPriceLevel2()を実行できません。

2つの関数を同時に実行でき、かつGUIのスレッドとは別のスレッドでTaskを実行するにはどのようにすればよいのでしょうか?
どうぞよろしくお願い致します。

環境:
Windows10 + Visual Studio 2015

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

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

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

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

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

guest

回答2

0

こんにちは。

このコードを関数化し

の内容が分かりませんが、async関数ではなく普通の関数にしたと仮定して回答します。

GetPriceLevel1()やGetPriceLevel2()の処理が次の1秒処理開始までに終了していないのではないでしょうか? その結果、同じクライアントからの同じ要求のシーケンスが複数入り混じって発生して要求と応答の対応が取れず、おかしくなっている可能性が疑わしく感じます。

前の処理が終わってから次の処理を開始するには、System.Windows.Forms.Timerを使うと簡単です。
こちらはメイン・スレッドで次処理を開始しますから、関数化した関数から抜けるまで次の処理は始まりません。

ちなみに、ConfigureAwait(false)は後続の処理をTask.Run()が起動したスレッドで行う指定だそうですからTask.Run()に指定した処理GetPriceLevel1()が終わってから次に進むことになります。


【蛇足ですが】
多くのサイトでConfigureAwait(false)を使うことが推奨されているようですが、メイン・スレッドと同期する機能がありがたいasync関数のその機能を完全にキャンセルするのなら、最初からasync関数にしない方が良いような気がします。単にTaskで十分です。そのほうが簡単ですし。

投稿2017/11/21 09:04

Chironian

総合スコア23272

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

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

退会済みユーザー

退会済みユーザー

2017/11/21 10:56

ご回答ありがとうございます。 >GetPriceLevel1()やGetPriceLevel2()の処理が次の1秒処理開始までに終了していないのではないでしょうか? いえ、それらの関数が次の1秒開始までに終了していない場合は次の処理をスルーするようにしています。 System.Windows.Forms.Timerですが、こちらは精度が悪いのと、タイマーを動かしているとGUIは固まってしまわないでしょうか? 蛇足の件ですが、なかなか難しいところです。 待ったほうがいい処理と、待たないでいい処理がごっちゃになっていて自分でもよくわからなくなっている状態です。 現状で欲しい機能としては、「2つのURLに同時にアクセスしたい」という点です。 質問の最初に示したコードでは、同時アクセスは実現できているのですが、GUIが数分固まってしまうことが多く、この点を解決したいのです。 「GetPriceLevel1()とGetPriceLevel2()で板情報を同時に取得し、Formに板情報を表示し、ボタンを押すと注文を実行する」みたいなことを実現したいのですが、同時処理をするとなぜかGUIが固まってしまいます。 順番にawaitでGetPriceLevel1()を実行、完了したらGetPriceLevel2()を実行とすればGUIが固まることはないのですが、これだと数ms単位で変化する板情報に時間差がでてしまって正確ではありません。 どのようにすればこれが実現できるのでしょうか・・・。
Chironian

2017/11/21 12:40

> いえ、それらの関数が次の1秒開始までに終了していない場合は次の処理をスルーするようにしています。 なるほど。その機能が適切に動作していたら問題ない筈ですね。 > System.Windows.Forms.Timerですが、こちらは精度が悪いのと、タイマーを動かしているとGUIは固まってしまわないでしょうか? タイマー処理を実行中は当然GUIは動かないです。Wait()しているから結構つらいですね。考え不足でした。 > 現状で欲しい機能としては、「2つのURLに同時にアクセスしたい」という点です。 「ここまではうまくいきました。ほぼ同時に2つの板にアクセスして情報を取得できています。」と書かれていますので、「2つのURLに同時にアクセスしたい」はできていると認識しています。 そして、1秒間隔でそれをやろうとするとGUIが固まる問題があると理解しています。ですので、疑うべきは「1秒間隔で実行している」手順です。それを提示されていないのでなんとも。
退会済みユーザー

退会済みユーザー

2017/11/23 07:22

ありがとうございます。 とりあえずkiichi54321さんの方法(WhenAll)を使う方法でデッドロックは回避できました。
guest

0

ベストアンサー

Task.WaitAllなんて使ったらそりゃ止まるでしょ。Waitしているのだから。

このような形で、非同期で待たないとだめ。

await Task.WhenAll(task..)

やりたいことを美しく実現しているのが多分これ。
http://engineering.grani.jp/entry/2017/04/06/163904

投稿2017/11/21 08:29

編集2017/11/21 09:33
kiichi54321

総合スコア1984

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

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

退会済みユーザー

退会済みユーザー

2017/11/21 09:09

ありがとうございます。 ざっと読んでみたのですが、Taskが呼ばれた後にGUIスレッドに戻らない仕様なのでしょうか? ValueTupleを使うとスレッドプールを有効活用するという記述が他の検索をしても見つかりませんでした。 並列実行はできているのですがGUIが固まってしまうのをなんとかしたい感じなのです。
kiichi54321

2017/11/21 09:22

そもそも的に、WaitAllを使っているのが悪の元凶という感じがします。初めに挙げたURLのように、WhenAllを使うようにすると期待どおりになるんじゃないかな。
退会済みユーザー

退会済みユーザー

2017/11/21 12:24

あ、すみません・・・WhenAllと書いてあったのですね。WaitAllだと勘違いしていました。 それで調べてみたら「TaskのWaitはデッドロックが起こるから使ったらいけない」と書いていますね。 WhenAllに変えてみたらデッドロックが起こらなくなりました!
guest

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

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

ただいまの回答率
85.48%

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

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

質問する

関連した質問