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

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

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

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

Q&A

6回答

13376閲覧

C# Taskで直列に処理を実行したい

poge

総合スコア15

C#

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

0グッド

0クリップ

投稿2018/09/14 05:37

編集2022/01/12 10:55

環境
C# 7.0
.NET Framework 4.6.1

※質問の内容が悪かったようなので、コードを更新しました。

以下コードにて、Web上をクロールしながら、
並行してCSVのダウンロードを行おうとしています。

C#

1var taskList = new List<Task<bool>>(); 2while(true){ 3 webをクロールする処理 4 taskList.Add(Task.Run(()=> CSVファイルをダウンロードする処理)); 5 終了条件を満たすとbreak 6} 7Task.WaitAll(taskList);

CSVをダウンロードする処理は、クロール中も並行して行い、
クロール完了後にダウンロードが残っている場合には、終了を待機します。

上記のコードで要件は満たしているのですが、スペックの低いPCで実行するため、
CSVファイルをダウンロードする処理が複数並列して走らないようにしたいです。
また、可能であれば、CSVファイルのダウンロードは、taskListに追加した順に実行したいです。

イメージとしては、1つのスレッドに対して、ダウンロードのキューを追加していきたいです。

===
以下、元の質問

以下のコードにて、ループで実行したい処理を100回回し切ったのちに、
100個の重い処理が全て完了するまで待機しています。

C#

1var taskList = new List<Task<bool>>(); 2for(int i = 0; i < 100; i++){ 3 ループで実行したい処理 4 taskList.Add(Task.Run(()=> 重い処理)); 5} 6Task.WaitAll(taskList);

上記のコードの場合、スレッドプールからスレッドが割り当てられ、重い処理が並列実行されてしまいます。
tasklistの100個のタスクを、直列に実行するにはどうしたら良いでしょうか。
※単に重い処理をawaitで待ってしまうと、重い処理に引きずられてループで実行したい処理も待機してしまいます。

よろしくお願いします。

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

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

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

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

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

tonkun4os

2018/09/14 05:52

ループわけないとダメでは、自分で書いてる文面の通りに処理に成っていないことを検証してください。
poge

2018/09/14 06:48

ループで実行したい処理*100は先に完了させたいです。 重い処理*100は最後に完了を待機したいです。 ただし、各重い処理を開始するためには、ループで実行したい処理側の結果が必要となります。 ループがすべて終わった後に、重い処理*100を直列で実行してもよいのですが、ループで実行したい処理側もそれなりに時間がかかるので、重い処理にも手はつけておきたいのです。
tonkun4os

2018/09/14 07:09

じゃ先にした処理の結果を変数に取っておいて、重いTaskのコンストラクタで渡すしかないのでは、(もちろんループは分ける)、言てる事判ります?
tonkun4os

2018/09/15 01:55

IsAliveプロパティで起動したタスクが幾つあって終了したタスクがあれば新たに起動するとか・・・
guest

回答6

0

Task は便利ですがスレッドプールを利用するので優先順位が付けられません。
このケースでは、優先度の高いループ処理のスレッドと、優先度の低い重い処理のスレッドを自前で用意したほうが簡単だと思います。

投稿2018/09/14 07:34

hihijiji

総合スコア4150

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

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

poge

2018/09/14 23:56

回答ありがとうございます。 すみません、質問が悪かったようなので、質問内容を修正しました。
hihijiji

2018/09/15 01:52

Task.Run(()=> CSVファイルをダウンロードする処理) でTaskがスレッドプールに入り、随時走ります。 例えば、 クロール側ではTaskを作らず処理待ちキューにUriを追加します。 ダウンロード側はシングルタスクとし、中では処理待ちキューが無くなるまでダウンロード処理をループします。 処理待ちキューはBlockingCollection<Uri> 等のコレクションを使って作るといいでしょう。
guest

0

やりたいことは、TPL Dataflowでやりたいことかな?処理のパイプランを作りたい的な解釈で。

https://ufcpp.net/study/csharp/AsyncVariation.html

今どきだと、System.Threading.Channels を使うと良さそう。
https://qiita.com/skitoy4321/items/c19ca3dc7624a7049fd5

投稿2018/09/14 12:39

kiichi54321

総合スコア1984

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

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

poge

2018/09/14 23:56

回答ありがとうございます。 すみません、質問が悪かったようなので、質問内容を修正しました。
guest

0

これでどうでしょう?

cs

1Action a = () => { }; 2for(int i = 0; i < 100; i++){ 3 ループで実行したい処理 4 a += () => 重い処理; 5} 6await Task.Run(a);

投稿2018/09/14 23:16

gaya-K

総合スコア449

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

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

poge

2018/09/14 23:56

回答ありがとうございます。 すみません、質問が悪かったようなので、質問内容を修正しました。
guest

0

こんな感じ

  動的配列Aを取ります。   最初のループ100回     ループで実行したい処理の結果を配列Aに追加   最初のループ終わり   2番目のループ100回     Taskのプロパティへ上記配列Aの2番目のループのカウント位置の値を設定(ここが判りにくいでしょうが)     Taskのスレッド起動   2番目のループ終わり   全スレッド終わるまで待機

では

投稿2018/09/14 07:20

tonkun4os

総合スコア321

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

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

poge

2018/09/14 23:56

回答ありがとうございます。 すみません、質問が悪かったようなので、質問内容を修正しました。
guest

0

直列実行したかったら、delegateをListにつっこんでいって、2回のループで順次実行すればいいだけだと思います。

ただ、ジャストアイデアですが、TaskにContinueWithしていけばいいのでは?

csharp

1 2var first = new Task<T>(); 3var t = first; 4for(var i = 0 ; i < 10 ; i++) 5{ 6 t = t.ContinueWith(lambda); 7}

このあとにfirstを実行すると実行されていかないかなー。

動作するかどうかは確認してません。

投稿2018/09/14 06:46

papinianus

総合スコア12705

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

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

poge

2018/09/14 23:57

回答ありがとうございます。 すみません、質問が悪かったようなので、質問内容を修正しました。
papinianus

2018/09/18 14:38

hihijijiさんの回答がベストっぽい気がしますが、それはさておき。 全ての回答にこのコメントがついていますが、追記を拝見しても、回答に修正すべき点がないように思われます。それぞれの回答に同じコメントではなく、それぞれ追記した処理(ダウンロード)に対してどのように質問者様の想定にあわないのかをおっしゃっていただかないと、新たな回答や回答の編集は得られないと思います
guest

0

やりたいことをそのまま反映するとタスクを立てる意味がないので下記のように変更できるかと思います。

//var taskList = new List<Task<bool>>(); ←並列実行しないので不要 for(int i = 0; i < 100; i++){ ループで実行したい処理 //taskList.Add(Task.Run(()=> 重い処理));←重い処理をこのループで実行してしまうため不要   重い処理 } //Task.WaitAll(taskList);←並列実行しないので不要

投稿2018/09/14 05:51

n_takapyon

総合スコア443

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

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

n_takapyon

2018/09/14 05:53

ただ、処理の流れを想像するかぎり、ループ内の処理は比較的軽いものであり、先に処理してしまいたいという意図があるように感じます。
poge

2018/09/14 06:50

その通りです。 ループで実行したい処理*100は先に完了させたいです。 重い処理*100は最後に完了を待機したいです。 ただし、各重い処理を開始するためには、ループで実行したい処理側の結果が必要となります。 ループがすべて終わった後に、重い処理*100を直列で実行してもよいのですが、ループで実行したい処理側もそれなりに時間がかかるので、重い処理にも手はつけておきたいです。 重い処理を直列にしたい理由は、重い処理を並列にすると、スペック不足でPCが重くなるためです。 重い処理側のTaskにセマフォ等を使用して制限をかけるのがいいのでしょうか。
n_takapyon

2018/09/14 09:40

ループ内に書き過ぎているということはありませんか? 重たい=処理が多い もしくは ムダがある なのでリファクタを検討してみるのも一つです。
poge

2018/09/14 23:57

回答ありがとうございます。 すみません、質問が悪かったようなので、質問内容を修正しました。
guest

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

まだベストアンサーが選ばれていません

会員登録して回答してみよう

アカウントをお持ちの方は

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

ただいまの回答率
85.50%

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

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

質問する

関連した質問