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

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

ただいまの
回答率

89.52%

2つのスレッドの処理時間について

受付中

回答 4

投稿 編集

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

nobysanz

score 39

お世話になります。 スレッドの優先度について質問です。C#で2つのスレッドでフォームを表示する処理を行っております。 2つのスレッドをスレッドプールから順番に起動し、処理時間をログファイルに出力しました。 結果を見ると、起動時間は同じなのに、必ずform生成後に、2番目のスレッドのform生成が完了しております。2つのスレッド間で、排他制御などされているのでしょうか?たとえば、form作成時はリソースの排他がかかるとか? スレッドAの処理の間に、スレッドBの処理がはいると考えておりました。 ご教授お願いします。

Thread Run St:06:59:37.0947348(スレッドA開始)
Thread Run St:06:59:37.0947348(スレッドB開始)
Thread Run --:06:59:37.1103348(スレッドA①フォーム作成完了 new Form)
Thread Run --:06:59:37.1103348(スレッドA②処理)
Thread Run --:06:59:37.1103348(スレッドA②処理)
Thread Run --:06:59:37.1103348(スレッドA②処理)
Thread Run --:06:59:37.1103348(スレッドA②処理)
Thread Run --:06:59:37.1103348(スレッドA③フォーム表示 Application.Run)
Thread Run --:06:59:37.1259348(スレッドB①フォーム作成完了 new Form)
Thread Run --:06:59:37.1259348(スレッドB②処理)
Thread Run --:06:59:37.1259348(スレッドB②処理)
Thread Run --:06:59:37.1259348(スレッドB②処理)
Thread Run --:06:59:37.1259348(スレッドB②処理)
Thread Run --:06:59:37.1259348(スレッドB③フォーム表示 Application.Run)

2015-12-07 追記です(コメントありがとうございます!)
このようなことをしているのは、本質問の発端となった事象を再現させたく。
検証しておりました。https://teratail.com/questions/21087 私の認識と、再現させるには以下と考えますがいかがでしょうか?

1 コメントの内容にて以下のような認識を持ちました。

(1)Formの生成は、リソースがロックされる。
同じタイミングで実行した2つのスレッドA,B内でFormを生成すると、
スレッドAのForm生成後に、スレッドBのForm生成が実行される。

(2)オブジェクトのnewは内部的に排他制御がかかる。
スレッドAでnew処理を実行した場合、newが完了するまで、スレッドBが待機される。

(3)タスクスイッチが頻繁発生しない タスクスイッチは、オーバーヘッドがかかる処理のため、
数100msの処理時間ではタスクスイッチしない。

(4)スレッドプールで同じキューに入った場合は、スレッド完了までまたされる。

(5)マルチコアで特に排他制御をすることもなく同時に開始した処理が、
15ミリ秒以上待たされるということは考えにくい。

2 スレッドAのフォーム作成完了後の処理中に、スレッドBのフォーム作成を実行させるには?
(1) Sleep()をいれる → だめでした。
(2) Thread.Yield()をいれる → だめでした。  
(3) ファイルアクセス処理をする → 検証中

 ほかになにか考えられますでしょうか?

ご教授お願いいたします。

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

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

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

    クリップを取り消します

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

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

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

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

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

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

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

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

    質問の評価を下げる

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

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

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

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

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

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

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

    詳細な説明はこちら

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

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

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

回答 4

+3

こんにちは。
なんか少しディープですね…

以下予想です。
ご存じかもしれませんが、背景としてWindowsフォームのコントロールはUIスレッドからしか操作できません。

その関係でフォームの生成時には、WindowsFormsSynchronizationContextの設定が走ります。
おそらく、そのタイミングでTheadContext.MarshalingControlにアクセスするんですが
こいつがスレッドセーフな作りになっているのでスレッド間で待機し合うのではないかと。

<参考>
WindowsFormsSynchronizationContext.Constructor
http://referencesource.microsoft.com/#System.Windows.Forms/winforms/Managed/System/WinForms/WindowsFormsSynchronizationContext.cs,d37865bac8deb498  

Application.TheadContext.MarshalingControl
http://referencesource.microsoft.com/#System.Windows.Forms/winforms/Managed/System/WinForms/Application.cs,150fc91c7f8e7176  

ということで、私の予想では
質問者さんの推察どおり、「フォーム生成時にリソースロックしているため。」です。
間違ってたらすみません!

投稿

編集

  • 回答の評価を上げる

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

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

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

  • 回答の評価を下げる

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

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

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

  • 2015/12/04 11:06

    質問と関係ありませんが、改行については、以下の通りのようです。

    https://teratail.com/blog/article/ba21
    > 任意に改行を行う場合は末尾に半角スペース2個を挿入ください。また空白行は詰まります。

    キャンセル

  • 2015/12/04 11:11

    ありがとうございます。改行調整してみました。

    キャンセル

  • 2015/12/04 13:08

    コメントありがとうございます!
    内容確認します。

    キャンセル

0

あり得る結果ですが、排他処理がかかっているわけではありません。 スレッドは走り出すとしばらくはCPUを掴み続ける傾向があります。タスクスイッチは重いオーバーヘッドなのでそんなに頻繁にはかからないのです。 さてオブジェクトのnewには内部的に排他制御がかかっているのは想像がつくと思います。空いているメモリ領域を探して見つけたら確保して、という操作が競合してはまずいですからね。 これによりスレッドBは待機状態に入ります。

タスクスイッチがそう頻繁には起こらないせいでAは走り続けBはしばらく止まったままになり、ログのような結果になります。 しかしそうなることは保証されていません。

強制的にタスクスイッチを起こすこともできます。Thread.Yield()を呼ぶことで、待機中のスレッドがいればそちらにCPU使用権を渡せます。

投稿

  • 回答の評価を上げる

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

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

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

  • 回答の評価を下げる

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

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

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

  • 2015/12/04 09:13

    コメントありがとうございます。頻繁にタスクスイッチはされないのですね。
    動作PCは、4コアのプロセッサですが、スレッドA、スレッドBが、各コアに割り振られた場合は、並列で動作することは、考えられますでしょうか?
    ご教授お願いいたします。

    キャンセル

0

マルチコアで特に排他制御をすることもなく同時に開始した処理が、15ミリ秒以上待たされるということはあまり考えられません。

スレッドプールで同じキューに入ってしまった場合は、先に実行したスレッドの処理が終わる(制御を返す)までは次のスレッドの実行は待たされるので、ご質問にあるような現象が発生する場合があります。

ただ、キューは複数持っているはずなので、同じキューに入るということも通常考えられないのですが。

スレッドプールのキューの数を制限したりはしていませんか?


追記

問題を切り分けるために。試しにスレッドプールではなく通常のThreadクラスを使ってみてはどうでしょうか。 (というか、フォームのように元々作成コストが高い処理の場合はスレッドプールを使うメリットがあまりないので、通常のThreadクラスで問題ないと思います。むしろ負荷の高い処理でキューを消費するのはもったいない気がします。)

投稿

編集

  • 回答の評価を上げる

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

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

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

  • 回答の評価を下げる

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

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

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

  • 2015/12/04 13:09

    コメントありがとうございます!
    スレッドに変更して試してみます。

    キャンセル

0

C#で2つのスレッドでフォームを表示する処理

これって普通にできるのでしたっけ?

Windowsのウィンドウを別スレッドで生成し制御する場合、それぞれのスレッドでメッセージ・ループを回す必要があります。nobysanzさんがどのような方法でフォームを生成されたのか分かりませんが、メッセージ・ループを回してくれるような生成方法で生成されたでしょうか?(すいません、C#ではやったことないので、C#で出来るかどうかさえ分かりません。C/C++ではできますし、やったことありますが。)

もし、そこまでされてないのであれば、それぞれのフォームはメイン・スレッドで生成される筈です。 でないと、メイン・スレッドで回っているメッセージ・ループがそのウィンド宛のメッセージを受け取れない筈です。

そして、C#の場合、「フォーム コントロールへのアクセスは、本質的にスレッド セーフではありません。」となってます。同じフォームのコントロールを別スレッドからアクセスする場合は排他制御等が必要と言う意味ですが、異なるフォーム同士でもフォームを管理しているデータが本当にスレッド安全なのか、少し心配です。

nobysanzさんがおっしゃっているような動きをするということは、C#処理系側でスレッド安全になるよう排他制御している可能性はあると思います。


【追記】
Application.Run()は「現在のスレッドで標準のアプリケーション メッセージ ループの実行を開始します。」と記載されてました。 つまり、ここでメッセージ・ループを回すので、それぞれのスレッドにてメッセージ・ループを回っていますね。

さて、普通にメッセージ・ループを回すだけであれば特に他のスレッドと同期させる必要はありません。 ということは、特に排他制御していないにも関わらず、スレッドAは起動から約15mSecで処理を開始し、スレッドBは起動から約30mSecで処理を開始していることになります。 まるでシングル・スレッド動作してますね。マルチコアでそれは考えにくいです。

ところで、new Form;は、C#のフォーム・クラスのインスタンスを生成します。ここでCreateWindow()されているかも知れませんが、まだメッセージ・ループを周り始めていませんので、実際のウィンドウの生成・描画は始まっていません。Run()内でウィンドウ・メッセージを処理しつつ、生成・描画が進行します。

もしも、new Form;でMutexを獲得し、Run()でウィンドウが本当に生成・描画されてから解放しているなんてことになっていたと仮定すると、説明が付きますね。 (同じスレッド内で複数new Form;する時、同じスレッドが既にMutexを獲得しているケースではブロックされませんから。)

(スレッドA開始)と(スレッドB開始)はそれぞれのスレッド内で出力してますか? それとも、各スレッドを起動したスレッドで出力してますか? もし、後者なら、試しに各スレッドでnew Form;を行う前にもログ出力してみませんか? スレッドに制御が渡ってから、new Form;が完了するまでの時間が気になります。 もし、それが片方は15mSec、もう一方が30mSecで安定しているようでしたら、.NETが何か15mSec単位のタイムスケジュールに同期するような処理をnew Form;に仕込んでいることになります。
.NETは思わぬところで、異常な処理をしていることがありましたので、油断できません。

あっ、異常な処理で、思い出しました。その異常な処理で困ったのは.NETのファイル・ストリームでした。ファイルが大きくなると、アペンド処理が異常に遅くなるという本当に不可解な現象でした。Windows APIを直接呼び出して回避したことがあります。

もし、ログを同じファイルへファイル・ストリームを使って出力しているのでしたら、それが何か悪さしていることも考えられます。当然排他制御されてますし。 もし、同じファイルに出力しているようでしたら、別ファイルに出してみるのも手です。 でも、.NET経由なので安心はできませんが...


【余談】
なんか改行が使いにくくなってますね。>teratail
って、 eripongさんが改行方法を書いてくれてました!! ありがとう。


【追記】

2015-12-07 追記です
(中略) 2 スレッドAのフォーム作成完了後の処理中に、スレッドBのフォーム作成を実行させるには? 
に今気が付きました。

もし、本当に.NETがフォーム生成中(new Form~フォーム表示)の間、排他制御していたら、無理な気がします。
確認するために、スレッドAのnew Formの直前でSleep(5)としてみて、スレッドAとスレッドBのフォーム作成タイミングが逆にならないでしょうか? もし、そうなるなら、これは回避できないように感じます。 後は.NETのバージョンを上げてみるくらいしか思いつきませんでした。

投稿

編集

  • 回答の評価を上げる

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

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

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

  • 回答の評価を下げる

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

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

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

  • 2015/12/04 13:06

    コメントありがとうございます。
    本件の発信元は、以下の質問が発端です。リンク先にソースをあります。
    https://teratail.com/questions/21087

    キャンセル

  • 2015/12/04 13:53

    なるほど。「メッセージ・ループを回してくれるような生成方法で生成」されているのですね。失礼しました。
    それを前提に、回答へ追記します。

    キャンセル

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

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

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