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

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

ただいまの
回答率

87.34%

子タスクが複数ある場合に一方の子タスクが完了待ちしてこない

解決済

回答 1

投稿 編集

  • 評価
  • クリップ 0
  • VIEW 2,070

score 19

前提・実現したいこと

お世話になります。
C#で2つのタスクA,Bを並列で動作させている状態で、さらに各タスクが子タスクを持っているシステムを作成しています。わかりやすいように以下では例を用いて説明させて頂きます。

①タスクA:ネットワーク接続確認を行う。
②タスクB:ネットワーク越しのフォルダ間コピーを行う。
③子タスク:
タスクA,Bで用いる子タスクは共通のクラスを用いており、
・タスクAの子タスクは"ping 接続先IPアドレス"
・タスクBの子タスクは"copy コピー元フォルダパス コピー先フォルダパス"
というコマンドをSystem.Diagnostics.Processで実行させ、実行完了するまで待つタスクです。

タスクAでは子タスクのpingの実行結果を用いて接続判定(接続or切断)を行います。
タスクBでは子タスクのcopyの実行結果を用いてコピー成否判定(成功or失敗)を行います。
タスクA,Bともに子タスクの実行完了はawaitで待ち、awaitの次の行で上記の結果を用いた判定を
行わせています。

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

タスクAでは子タスクのawaitで待ち完了→次の行で結果を用いた判定ができています。
タスクBでは子タスクのawaitで待ち→いつまで経っても次の行に来ないため結果を用いた判定ができません。
タスクBでも、判定ができるようにしたいのですが原因がわからず、ご教授をお願い致します。

該当のソースコード

以下にコードを記載します。
下記の★1(GetIsConnect()内のawaitの次の行)には来ますが、★2(CopyFolder()内のawaitの次の行)には
いつまで経っても来ません。ここに来るようにしたいのですが原因がわからずで、ご教授をお願い致します。
なお、ExeCommandTask()はタスクA,Bの子タスクであるため、StartNew時にAttachedToParent指定
しています。
タスク設計に熟達しておらず、使い方が誤っている箇所もあるかと思いますが、それも併せて教えて
頂けますと幸いです。よろしくお願い致します。

------------------------- メインクラス:CMainのメンバ変数,関数 -------------------------

◆/* @brief タスクA,Bのキャンセルトークンソース */ 
CancellationTokenSource m_CTSTaskA = new CancellationTokenSource();
CancellationTokenSource m_CTSTaskB = new CancellationTokenSource();

◆/* @brief タスクA,Bを非同期で並列に実行する */ 
public async void ExeTaskAandB()
{
var t1 = TaskA(); 
var t2 = TaskB();
await Task.WhenAll(t1, t2);
}

◆/* @brief タスクA:ネットワーク接続確認を行う */ 
private Task TaskA()
{
return Task.Factory.StartNew(() => {
while (true) {
if (GetIsConnect().Result == 1) {// 接続と判定}
else {// 切断と判定}
Task.Delay(2000, m_CTSTaskA.Token);
}
}, m_CTSTaskA.Token);
}

◆/* @brief ネットワーク接続確認をプロセスで行う */ 
private async Task<int> GetIsConnect()
{
CCommon common = new CCommon();
string result = await common.ExeCommandTask(m_CTSTaskA, "ping 接続先IPアドレス");
if (result.Contains(ping送信成功を示す特定の文字列)) {return 1;} ・・・★1.ここには来る
else {return 0;}
}

◆/* @brief タスクB:ネットワーク越しのフォルダ間コピーを行う */ 
private Task TaskB()
{
return Task.Factory.StartNew(() => {
CCopy copy = new CCopy(); 
while (true) {
if (copy.CopyFolder(m_CTSTaskB).Result == 1) {// コピー成功と判定}
else {// 失敗と判定}
Task.Delay(500, m_CTSTaskB.Token);
}
}, m_CTSTaskB.Token);
}

------------------------- コピークラス:CCopyのメンバ関数 -------------------------

◆/* @brief ネットワーク越しのフォルダ間コピーを行う */ 
public async Task<int> CopyFolder(CancellationTokenSource cts)
{
CCommon common = new CCommon();
string result = await common.ExeCommandTask(cts, "copy コピー元フォルダ コピー先フォルダ");
if (result.Contains(copy成功を示す特定の文字列)) {return 1;} ・・・★2.ここには来ない
else {return 0;}
}

------------------------- 共通クラス:CCommonのメンバ関数 -------------------------

◆/* @brief コマンドをプロセスで実行するタスク */ 
public Task<string> ExeCommandTask(CancellationTokenSource cts, string command)
{
return Task.Factory.StartNew(() => {
// コマンドをプロセスで実行する際の設定
System.Diagnostics.Process process = new System.Diagnostics.Process();
process.StartInfo.FileName = "cmd";
process.StartInfo.Arguments = "/c " + command;
process.StartInfo.CreateNoWindow = true;
process.StartInfo.UseShellExecute = false;
process.StartInfo.RedirectStandardOutput = true;

try {
// コマンドをOSのプロセスで実行し、1秒ごとに実行完了待ちを行い、同時にタスクキャンセル要求が
// ないか確認する
// 実行完了すれば実行結果をresultに得て、プロセスをクローズする
process.Start();
string result = process.StandardOutput.ReadToEnd();

while (true) {
process.WaitForExit(1000);
if (process.HasExited) {break;}
cts.Token.ThrowIfCancellationRequested();
}
process.Close();
return result;
}

// タスクキャンセル要求があった際に実行中のプロセスがある場合は強制終了する
catch (OperationCanceledException) {
if (!process.HasExited) {process.Kill();}
return "";
}
}, cts.Token, TaskCreationOptions.AttachedToParent, TaskScheduler.Default);
}

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

C#,Visual Studio2015

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

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

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

    クリップを取り消します

  • 良い質問の評価を上げる

    以下のような質問は評価を上げましょう

    • 質問内容が明確
    • 自分も答えを知りたい
    • 質問者以外のユーザにも役立つ

    評価が高い質問は、TOPページの「注目」タブのフィードに表示されやすくなります。

    質問の評価を上げたことを取り消します

  • 評価を下げられる数の上限に達しました

    評価を下げることができません

    • 1日5回まで評価を下げられます
    • 1日に1ユーザに対して2回まで評価を下げられます

    質問の評価を下げる

    teratailでは下記のような質問を「具体的に困っていることがない質問」、「サイトポリシーに違反する質問」と定義し、推奨していません。

    • プログラミングに関係のない質問
    • やってほしいことだけを記載した丸投げの質問
    • 問題・課題が含まれていない質問
    • 意図的に内容が抹消された質問
    • 過去に投稿した質問と同じ内容の質問
    • 広告と受け取られるような投稿

    評価が下がると、TOPページの「アクティブ」「注目」タブのフィードに表示されにくくなります。

    質問の評価を下げたことを取り消します

    この機能は開放されていません

    評価を下げる条件を満たしてません

    評価を下げる理由を選択してください

    詳細な説明はこちら

    上記に当てはまらず、質問内容が明確になっていない質問には「情報の追加・修正依頼」機能からコメントをしてください。

    質問の評価を下げる機能の利用条件

    この機能を利用するためには、以下の事項を行う必要があります。

質問への追記・修正、ベストアンサー選択の依頼

  • tamoto

    2016/08/23 11:49

    [該当のソースコード] ですが、GetIsConnect()がTask<int>を返しているのに == 0 で比較されていたり、 async void の CopyFolder() に return 0 を記述していたりと、根本的にコンパイルが通るコードになっていないようです。このままでは設計意図が掴めないので、修正をお願いします。

    キャンセル

  • matsu1

    2016/08/23 13:14

    すみません、コードが誤っておりましたので修正しておきました。
    こちらに記載したコードは実際の開発コードを必要最低限簡易化したものですが、現象が再現しない(★2にくる)ので、見直します。
    お手数ですが、のちほどよろしくお願い致します。

    キャンセル

  • matsu1

    2016/08/23 17:08

    調査しておりましたが、GUI周りのコードに問題があり、それが原因で★2に来ないことがわかりました。上記記載のコードは問題ないかと思われます。
    近日中に本質問の削除依頼をします。お手数をおかけしました。

    キャンセル

回答 1

check解決した方法

0

調査しておりましたが、GUI周りのコードに問題があり、それが原因で★2に来ないことがわかりました。上記記載のコードは問題ないかと思われますので自己解決とします。 

投稿

  • 回答の評価を上げる

    以下のような回答は評価を上げましょう

    • 正しい回答
    • わかりやすい回答
    • ためになる回答

    評価が高い回答ほどページの上位に表示されます。

  • 回答の評価を下げる

    下記のような回答は推奨されていません。

    • 間違っている回答
    • 質問の回答になっていない投稿
    • スパムや攻撃的な表現を用いた投稿

    評価を下げる際はその理由を明確に伝え、適切な回答に修正してもらいましょう。

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

  • ただいまの回答率 87.34%
  • 質問をまとめることで、思考を整理して素早く解決
  • テンプレート機能で、簡単に質問をまとめられる

関連した質問

同じタグがついた質問を見る