C# と VB.NET の質問掲示板

ASP.NET、C++/CLI、Java 何でもどうぞ

C# と VB.NET の入門サイト

Re[17]: Control.Invokeが使えない件。 [1]


(過去ログ 18 を表示中)

[トピック内 52 記事 (21 - 40 表示)]  << 0 | 1 | 2 >>

■6867 / inTopicNo.21)  Re[8]: Control.Invokeが使えない件。
  
□投稿者/ れい (67回)-(2007/08/25(Sat) 10:32:05)
2007/08/25(Sat) 10:38:40 編集(投稿者)

あ、あれ?
はじめっから全然伝わってない…。

> 結局、Control.Invoke() を使うことによる不都合は↓のどちらのパターンであるって話なんでしょう?
> ・Control.Invoke() の投げ先が正常な状態にあっても常に起き得る。
> ・Control.Invoke() の投げ先であるフォーム/コントロールが廃棄途上にある時に起きる。

後者です。

私、日本語が不自由っぽいですね。
うまく伝えられなくてすみません。

廃棄中のControlに他のスレッドからInvokeすると止まることがあります。
Invoke先は他スレッドなので、Invoke先Controlが廃棄中であるかどうか、
Invoke元スレッドから正確に判断することができません。
なので、Control.Invokeを安全に使えるケースが存在しない、と言ってます。

伝わったでしょうか?

引用返信 編集キー/
■6868 / inTopicNo.22)  Re[9]: Control.Invokeが使えない件。
□投稿者/ 渋木宏明(ひどり) (313回)-(2007/08/25(Sat) 11:54:09)
渋木宏明(ひどり) さんの Web サイト
> 廃棄中のControlに他のスレッドからInvokeすると止まることがあります。
> Invoke先は他スレッドなので、Invoke先Controlが廃棄中であるかどうか、
> Invoke元スレッドから正確に判断することができません。
> なので、Control.Invokeを安全に使えるケースが存在しない、と言ってます。
>
> 伝わったでしょうか?

伝わりました ;-)

でも、「そりゃしょーがないじゃん」と思います (^^;

以前の投稿でも書いていますが、廃棄途上の相手に Control.Invoke() しなければならないような状況を作り出さないように注意しているし、なるべく避けるように仕様と設計を調整しています。

「Control.Invoke() だから」そう感じるのかもしれませんが、「廃棄中のオブジェクトのメソッド呼び出し」なんか .NET でははなから保証されてません。

「オブジェクトの実装」として ObjectDisposeException() やらを投げてよこすことはありますが、所詮はランタイムの管理範囲外であるオブジェクトの振る舞いなので、何が起きるかはそのオブジェクトの実装次第です。

# 「ブロックしないでせめて例外をあげてくれ」という話なら賛同します。

Control.Invoke() が「同期」しているのはプライマリスレッド/ワーカスレッドの実行コンテキストであって、Control.Invoke() 先のオブジェクトの状態まで保証するもんではないと思います。

> やっぱり、Control.Invokeは私には使えそうにないです…。
> 1/500で止まるのは見過ごせないし、
> どう同期組んでいいのかわからなすぎる。

常態で十分に機能しているんで、特に問題を感じません。

フォームやコントロールの廃棄中に Control.Invoke() を許してしまうような設計というか構造がまずいと思うな。

通信スレッドから Control.Invoke() でプライマリスレッドの管理するウィンドウにログを表示するようなアプリを24時間以上稼動させたこともありますが、Control.Invoke() でフリーズとかデッドロックってのには出くわしたことが無いです。
引用返信 編集キー/
■6869 / inTopicNo.23)  Re[9]: Control.Invokeが使えない件。
□投稿者/ NyaRuRu (17回)-(2007/08/25(Sat) 12:01:05)
2007/08/25(Sat) 12:27:44 編集(投稿者)
2007/08/25(Sat) 12:07:58 編集(投稿者)

No6867 (れい さん) に返信
> なので、Control.Invokeを安全に使えるケースが存在しない、と言ってます。
>
> 伝わったでしょうか?

れいさんは「Control.Invoke は使えない」と主張されたいようですが,私も「Control.Invoke を使って問題になることがある」という点は,今までの話を見た限り,そうなんだろうなぁと思います.
私がそこを「はいそーですね」と書かないかられいさんが困っているなら書いておきますよ.はい.
ただそこは私にとって別にどうでもいいというか,よくある落とし穴のひとつみたいなもので,何が起きているのかの詳細の方がどちらかというと興味があるのです.だって Invoke の問題は将来こっそり Fix されるかもしれませんが,根本的な原因が何であったかは将来においても通用する重要な「失敗事例」なわけですし.そこを「まあ多分〜ですよね」で調べずにおいておくのがどうも嫌なだけです.
たとえば確率的に失敗するという意味では,using 構文も lock 構文も失敗で,この教訓は将来似たような実装をするときに自分でも生かしますよ.
http://d.hatena.ne.jp/NyaRuRu/20060605

> 廃棄中のControlに他のスレッドからInvokeすると止まることがあります。
> Invoke先は他スレッドなので、Invoke先Controlが廃棄中であるかどうか、
> Invoke元スレッドから正確に判断することができません。

デバッグに関するポリシーの違いかと.

私は,「廃棄中のControlに他のスレッドからInvokeすると止まることがある」という事例だけから,「廃棄中のControlに他のスレッドからInvokeしたのが原因」とは判断しないだけです.
あるいは,「廃棄中のControlに他のスレッドからInvokeしたのが原因」では,私の基準ではバグの原因としての粒度が大きすぎで,「問題があるのは確かだが原因が確定したわけではない」が妥当だと感じるわけですよ.
なのでそこを突っ込ませていただいているだけです.
れいさんは十分に調査されていて,その上で判断されているのかもしれませんが,その調べられている途中の情報が抜けている気がします.

>で、何回もやると何処かでWaitHandleのSetを忘れてフリーズしちゃうわけです。

特にこの辺.デバッガで待機ハンドルのステートを調べるぐらいはできると思うのですが,その辺をチェックされたということですよね?
RichEdit の例なんかは,GC スレッドからの Invoke まで絡んでいるように見えましたし.
私が興味があるのは,それが「何処か」ということの方です.

まとめ
(A)「いくつか Control.Invoke が原因でハングすること事例を見つけたので,信頼性の面から使いたくない」に対する回答「そうですね」
(B)「問題は (1) Controlの廃棄 (2)メッセージループの停止 辺りであろうと判断しても妥当だと思いますが、いかが?」に対する回答「それは推論では? もっと細かく何が起きているのか確かめられそう」

という感じです.れいさんが (A) にしか興味がなくて,Control.Invoke を封印すればそれでいいということであれば,私から申し上げることは特にないです.
引用返信 編集キー/
■6874 / inTopicNo.24)  Re[3]: Control.Invokeが使えない件。
□投稿者/ NyaRuRu (18回)-(2007/08/25(Sat) 15:59:59)
2007/08/25(Sat) 16:05:57 編集(投稿者)
2007/08/25(Sat) 16:00:19 編集(投稿者)
■No6864 (れい さん) に返信
> 2007/08/25(Sat) 09:37:52 編集(投稿者)
> 
> 
>>テストコードはhttp://bbs.wankuma.com/index.cgi?mode=al2&namber=6760の改造で詳細は後述。
> 
> .Net1.1用コードは以下。
> .Net2.0でもInherits以外同じ。
> なんか怪しい感じだけどツッコミは禁止。

このコードで確かにハングはするので,
無理矢理時間を作って調べてみましたが,見た感じ以下の描像とは異なるケースでしたよ.
もしかしたら下のケースもあるのかもしれませんが,10 回試して 10 回とも同じ理由でしたが,
下のような問題ではありませんでした.
環境は Windows Vista Ultimate Edition / Intel Core Duo T2500 です.

>メインスレッドでInvokeすると、サブスレッドのキューに該当するHWNDをセットした
Invoke用メッセージを投げます。
>メッセージ投げたら、WaitHandleで待ちます。
>サブスレッドはShowDialogの中でキューからInvoke用メッセージを拾い、
>HWNDに該当する窓にDispatchします。
>窓のWndProc内でInvoke用のメッセージだと認識して、
>メッセージのパラメータとかからデリゲートを作り、呼びます。
>デリゲートの実行が終わったら、WaitHandleをSetして、
>またメッセージループに戻ります。
>で、何回もやると何処かでWaitHandleのSetを忘れてフリーズしちゃうわけです。

実際にはメインスレッドのWaitHandle待ちは最大 1 秒のタイムアウトが設定されていて,
実はハングしたように見えて 1 秒ごとに Wait から復帰はしています.
無限に待っているわけではありませんでした.
ただ,間違った条件でぐるぐる 1 秒ごとに待っているので,
見た目無限に待機しているように見えるようです.

前回も書いたように,Win32 Window は何かのスレッドに所属しています.
これは GetWindowThreadProcessId API で調べることができます.
で,ある Win32 スレッドから,WinForms のコントロールに Invoke を行うとき,
次のような処理が行われるようです.

1) Win32 的な GetCurrentThread と,
   相手のコントロールが所属する Win32 スレッドの ID が一致するかどうか?
2) 一致しなければ,スレッド間通信を開始し(PostMessage) レスポンス待ちループへ.
2.0) レスポンス待ちループ
2.1) 相手のコントロールが所属する Win32 スレッドを取得
2.2) 相手のコントロールが所属する Win32 スレッドが終了しているかチェック.
2.2.1) 終了していればタイムアウト時間 1 msec で一回だけ応答を待って,
       応答があればリターン,タイムアウトなら Invoke 失敗
2.2.2) 終了していなければ,タイムアウト時間 1000 msec で応答を待って,
       応答があればリターン,タイムアウトなら 2.2 に戻る

さて,(1) と (2.1) で二回「相手のコントロールが所属する Win32 スレッドの ID」が
取得されていますが,この間に別スレッドで相手のコントロールが破棄されると,
返される ID が変わってしまうようです.
具体的な実装としては,コントロールが破棄されると,
「コントロールが所属する Win32 スレッドの ID」として GetCurrentThreadId API の値
を使うようでした.
HWND は既に無効なので GetWindowThreadProcessId が使えないから
こうしてるんでしょうかね.今回の場合明らかにまずいですが.

つまり何が起きるかというと,(1) の判定時はスレッド間通信が必要と見えて
レスポンス待ちループに突入してしまうのですが,
(2.1) の段階では相手のコントロールは GetCurrentThreadId に所属すると思ってしまう
わけです.
(2.2) の判定は,現在自分自身が走っているスレッドの生存判定になってしまい,
それが死んでいるわけもなく,結局 (2.2) と (2.2.2) を無限に行き来すると.
とりあえず私のところで起きていたのはそんな感じの問題に見えました.
端的に言えば,スレッド終了条件のチェックが間違っているということになります.

れいさんのお話によれば他にもハングするパターンがあるのかもしれませんが,
少なくともタイムアウトなしの WaitHandle で無限に待つというという実装で
無かったことは確かです.

参考までに,
http://bbs.wankuma.com/index.cgi?mode=al2&namber=6843#19
のコードを前半だけ書き換えてみたものを示します.
本来,別スレッドで表示された ChildForm を,メインスレッドから見れば,InvokeRequired は常に true なはずなのですが,
破棄のタイミングでこれが false に変化してしまうことがあるのが分かります.
最初 true だったタイミングである処理に入り,
その後 false になって別の情報を受け取ると,破綻することがあると.

Public Class Form1
    Inherits System.Windows.Forms.Form

    Public ChildForm As Form2
    Public InvokeIteration As Integer = 0
    Public FormCreateIteration As Integer = 0
    Public ChildFormCreated As Boolean
    Public Timer1 As System.Windows.Forms.Timer

    Private Sub Form1_Load(ByVal sender As Object, ByVal e As System.EventArgs) Handles MyBase.Load
        Me.components = New System.ComponentModel.Container
        Timer1 = New System.Windows.Forms.Timer(Me.components)
        Me.components.Add(Timer1)
        AddHandler Timer1.Tick, New EventHandler(AddressOf Timer1_Tick)
        Me.Timer1.Interval = 10
        Me.Timer1.Start()
    End Sub


    Private Sub Timer1_Tick(ByVal sender As System.Object, ByVal e As System.EventArgs)
        If ChildForm Is Nothing Then
            Dim subthread As New System.Threading.Thread(AddressOf Me.SubThreadProc)
            SyncLock Me
                ChildFormCreated = False
            End SyncLock
            subthread.Start()
            Me.Text = "Create"
            FormCreateIteration += 1
            InvokeIteration = 0
            While True
                SyncLock Me
                    If ChildFormCreated Then Exit Sub
                End SyncLock
            End While
        End If
        InvokeIteration += 1
        Me.Text = FormCreateIteration.ToString & ":" & InvokeIteration.ToString
        Try
            If Not ChildForm.InvokeRequired Then
                MessageBox.Show("InvokeRequired = false!")
            End If
        Catch ex As Exception
        End Try
    End Sub

    Private Sub SubThreadProc()
        ChildForm = New Form2
        ChildForm.MainForm = Me
        ChildForm.ShowDialog()
        ChildForm.Dispose()
        ChildForm = Nothing
    End Sub
End Class

申し訳ありませんが,私の方は時間的にこれが限界なので,後はそれっぽい人にお任せします.
Feedback を上げるかインシデントを使えば,
少なくとも今回のパターンについては修正を検討してもらえるかと.

その他のパターンは結局遭遇できなかったのでよくわかりませんでした.

引用返信 編集キー/
■6875 / inTopicNo.25)  Re[4]: Control.Invokeが使えない件。
□投稿者/ えムナウ (82回)-(2007/08/25(Sat) 22:24:28)
元々Form2側ではClose後にTextBoxに表示する意味がないのですから、
以下のようにFormClosedイベントをひっかければいいと思いますが、
問題ありますでしょうか?

Public Class Form2
Inherits System.Windows.Forms.Form

Private Delegate Sub WriteLineDelegate(ByVal text As String)
Public MainForm As Form1
Public TextBox1 As TextBox
Public formClose As Boolean = False

Public Sub WriteLine(ByVal text As String)
If Not formClose Then
If Me.InvokeRequired Then
Me.Invoke(New WriteLineDelegate(AddressOf WriteLine), New Object() {text})
Else
Me.Text = text
Me.TextBox1.Text = text
Me.TextBox1.ScrollToCaret()
Me.TextBox1.Focus()
End If
End If
End Sub

Private Sub Form2_Load(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles MyBase.Load
Me.components = New System.ComponentModel.Container
Dim Timer1 As New System.Windows.Forms.Timer(Me.components)
TextBox1 = New TextBox
Me.components.Add(Timer1)
Me.Controls.Add(TextBox1)
AddHandler Timer1.Tick, New EventHandler(AddressOf Timer1_Tick)
AddHandler Me.FormClosed, New FormClosedEventHandler(AddressOf Form2_FormClosed)
SyncLock MainForm.Form2SyncObject
MainForm.ChildFormCreated = True
End SyncLock
Timer1.Interval = New Random().Next(100, 1000)
Timer1.Start()
End Sub

Private Sub Timer1_Tick(ByVal sender As System.Object, ByVal e As System.EventArgs)
Me.Close()
End Sub

Private Sub Form2_FormClosed(ByVal sender As System.Object, ByVal e As System.Windows.Forms.FormClosedEventArgs)
formClose = True
End Sub
End Class

引用返信 編集キー/
■6876 / inTopicNo.26)  Re[5]: Control.Invokeが使えない件。
□投稿者/ えムナウ (83回)-(2007/08/25(Sat) 22:27:39)
あらPOST失敗した。

Public Class Form2
    Inherits System.Windows.Forms.Form

    Private Delegate Sub WriteLineDelegate(ByVal text As String)
    Public MainForm As Form1
    Public TextBox1 As TextBox
    Public formClose As Boolean = False

    Public Sub WriteLine(ByVal text As String)
        If Not formClose Then
            If Me.InvokeRequired Then
                Me.Invoke(New WriteLineDelegate(AddressOf WriteLine), New Object() {text})
            Else
                Me.Text = text
                Me.TextBox1.Text = text
                Me.TextBox1.ScrollToCaret()
                Me.TextBox1.Focus()
            End If
        End If
    End Sub

    Private Sub Form2_Load(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles MyBase.Load
        Me.components = New System.ComponentModel.Container
        Dim Timer1 As New System.Windows.Forms.Timer(Me.components)
        TextBox1 = New TextBox
        Me.components.Add(Timer1)
        Me.Controls.Add(TextBox1)
        AddHandler Timer1.Tick, New EventHandler(AddressOf Timer1_Tick)
        AddHandler Me.FormClosed, New FormClosedEventHandler(AddressOf Form2_FormClosed)
        SyncLock MainForm.Form2SyncObject
            MainForm.ChildFormCreated = True
        End SyncLock
        Timer1.Interval = New Random().Next(100, 1000)
        Timer1.Start()
    End Sub

    Private Sub Timer1_Tick(ByVal sender As System.Object, ByVal e As System.EventArgs)
        Me.Close()
    End Sub

    Private Sub Form2_FormClosed(ByVal sender As System.Object, ByVal e As System.Windows.Forms.FormClosedEventArgs)
        formClose = True
    End Sub
End Class

引用返信 編集キー/
■6877 / inTopicNo.27)  Re[6]: Control.Invokeが使えない件。
□投稿者/ えムナウ (84回)-(2007/08/25(Sat) 22:38:40)
Visual Basic 言語リファレンス SyncLock ステートメントHELP より

SyncLock は Me を使わないほうがいいようなので、
Form1内に Object を作ってあります。

引用返信 編集キー/
■6885 / inTopicNo.28)  Re[10]: Control.Invokeが使えない件。
□投稿者/ れい (68回)-(2007/08/26(Sun) 17:49:26)
No6868 (渋木宏明(ひどり) さん) に返信
>>伝わったでしょうか?
> 伝わりました ;-)

あ。やっと伝わりました。
で、ようやく次にいけます。

> フォームやコントロールの廃棄中に Control.Invoke() を許してしまうような設計というか構造がまずいと思うな。

これなんですが。
私もそう思い、いろいろ考えたんですが、
Invoke先がFormなので、いつ終了するかわかりません。
私の技術では、廃棄中にInvokeしないことを保証できないんです。

で、ここに「そんなことねーよ」とツッコミが欲しかったのですが、
えムナウさんのやりかたでもダメなんですよね。
引用返信 編集キー/
■6886 / inTopicNo.29)  Re[4]: Control.Invokeが使えない件。
□投稿者/ れい (69回)-(2007/08/26(Sun) 17:52:24)
2007/08/26(Sun) 18:08:01 編集(投稿者)

No6874 (NyaRuRu さん) に返信
> 無理矢理時間を作って調べてみましたが,見た感じ以下の描像とは異なるケースでしたよ.

お手数かけます。

> デバッグに関するポリシーの違いかと.

掲示板等での発言を見る限り、
Nyaruruさんや渋木宏明さんとポリシーがひどく違うことはないと思ってるんですが、
技術力の差がなんとも埋め難く。
私もより詳細に調べたかったんですが、IL読むのは今の私にはちょっと辛くて。
で、ネット断ちしてがんばって私も調べてたんですが、
今答えを見てしまいました。

> 実際にはメインスレッドのWaitHandle待ちは最大 1 秒のタイムアウトが設定されていて,

はい。確認しました。1secでループしてました。

> とりあえず私のところで起きていたのはそんな感じの問題に見えました.
> 端的に言えば,スレッド終了条件のチェックが間違っているということになります.

まだそこまで調べてなかったです。
一見ちゃんとチェックしてそうだったので、悩んでたんですが、
なるほどです。

まだ.Net1.1の方は見て無いですが、
違ってたら報告します。

Frameworkの中身が間違ってるなら、
渋木さんの言うように、へんなタイミングで呼ばないようにすべきなんですが。

引用返信 編集キー/
■6887 / inTopicNo.30)  Re[5]: Control.Invokeが使えない件。
□投稿者/ れい (70回)-(2007/08/26(Sun) 17:58:27)
2007/08/26(Sun) 18:53:18 編集(投稿者)

No6875 (えムナウ さん) に返信
> 元々Form2側ではClose後にTextBoxに表示する意味がないのですから、
> 以下のようにFormClosedイベントをひっかければいいと思いますが、
> 問題ありますでしょうか?

はい。
残念ながら、これでは止まる可能性があります。
とくにマルチプロセッサ、マルチスレッドプロセッサや負荷の高い状況では
止まる場合が考えられます。

formCloseチェックを潜り抜け、Invokeに入った後に
Formが閉じられ、Nyaruruさんの言う間違ったスレッド終了チェックが行われれば
止まります。

> Visual Basic 言語リファレンス SyncLock ステートメントHELP より
> SyncLock は Me を使わないほうがいいようなので、
> Form1内に Object を作ってあります。

ツッコミありがとうございます。

volatileがあればSynclockなんて要らないんですが、
作ってからvolatile相当のVBキーワードを知らないことに気づいて
あわててSynclockいれて誤魔化したのです。
なのでSynclock無くてもデバッグ環境ならちゃんと動いたり。

追記。
VBってvolatileないのね…。MyClassはあるくせに。
流石というか、なんというか。
引用返信 編集キー/
■6888 / inTopicNo.31)  Re[5]: Control.Invokeが使えない件。
□投稿者/ れい (71回)-(2007/08/26(Sun) 18:38:09)
No6886 (れい さん) に返信
> 2007/08/26(Sun) 18:08:01 編集(投稿者)
>>実際にはメインスレッドのWaitHandle待ちは最大 1 秒のタイムアウトが設定されていて,
> はい。確認しました。1secでループしてました。
> まだ.Net1.1の方は見て無いですが、
> 違ってたら報告します。

.Net1.1ではWaitOneの引数はUInt32.MaxValueでした。50日くらい。

まだ読んでる最中で、全然理解してないんで
これがそのままフリーズする時間であるとか原因であるとかは言えないんですが、
2.0とだいぶ違ってるようです。

引用返信 編集キー/
■6921 / inTopicNo.32)  Re[6]: Control.Invokeが使えない件。
□投稿者/ えムナウ (85回)-(2007/08/27(Mon) 14:46:12)
>残念ながら、これでは止まる可能性があります。
>とくにマルチプロセッサ、マルチスレッドプロセッサや負荷の高い状況では
>止まる場合が考えられます。
とりあえず私の環境の場合1000回以内に止まっていたものが、
夜通し動かしても止まらなかったのはご報告しておきます。
環境により違いはあると思いますが試してみたらどうでしょうか?

>formCloseチェックを潜り抜け、Invokeに入った後に
>Formが閉じられ、Nyaruruさんの言う間違ったスレッド終了チェックが行われれば
>止まります。
それならばformCloseチェックとform_Closingイベント処理中でロックかけたらどうでしょうか?
フォームがクローズされた後でNotingにクリアされる前にInvokeされることが原因だと思いますので。
引用返信 編集キー/
■6936 / inTopicNo.33)  Re[7]: Control.Invokeが使えない件。
□投稿者/ 困ったちゃん (9回)-(2007/08/27(Mon) 17:43:55)
2007/08/27(Mon) 19:09:24 編集(投稿者)
2007/08/27(Mon) 19:08:40 編集(投稿者)
2007/08/27(Mon) 17:53:57 編集(投稿者)

前身スレッド■No6760 でお世話になりました (困ったちゃん) です。

■No6887 (れい さん) を引用
> formCloseチェックを潜り抜け、Invokeに入った後に
> Formが閉じられ、Nyaruruさんの言う間違ったスレッド終了チェックが行われれば
> 止まります。No6921 (えムナウ さん) を引用
> それならばformCloseチェックとform_Closingイベント処理中でロックかけたらどうでしょうか?
> フォームがクローズされた後でNotingにクリアされる前にInvokeされることが原因だと思いますので。

「ロック」という言葉にインスパイアされて■No6876 のコード(前身スレッドの■No6822 と本質は同じ)
にちょっと姑息な修正を。
Closing イベントハンドラで Me.Close を BeginInvoke します。
これなら、溜まった Invoke が片付いた後に Close が実行されます。
本スレッドのテーマの『Control.Invokeが使えるか』からは逃避しているだけですけど。

    Private Sub Form2_FormClosed(ByVal sender As System.Object, ByVal e As System.Windows.Forms.FormClosedEventArgs)
        If Not formClose Then
            formClose = True
            Me.BeginInvoke(New System.Threading.ThreadStart(AddressOf Me.Close))
            e.Cancel = True
        End If
    End Sub

引用返信 編集キー/
■6945 / inTopicNo.34)  Re[7]: Control.Invokeが使えない件。
□投稿者/ れい (72回)-(2007/08/27(Mon) 19:52:01)
2007/08/27(Mon) 20:14:08 編集(投稿者)

No6921 (えムナウ さん) に返信
> >残念ながら、これでは止まる可能性があります。
> >とくにマルチプロセッサ、マルチスレッドプロセッサや負荷の高い状況では
> >止まる場合が考えられます。
> とりあえず私の環境の場合1000回以内に止まっていたものが、
> 夜通し動かしても止まらなかったのはご報告しておきます。
> 環境により違いはあると思いますが試してみたらどうでしょうか?

はい。
試してから回答しました。
詳しく調べてなかったので、ちょっとごまかした表現をしましたが、
closingやcloseのフラグは私も初期に試した方法で、
これでダメで困っていました。

私の環境、Windows Server 2003 2xXeonでは
.Net2.0では10000回に1回程度、
.Net1.1では1000回程度で停止しました。
とくに.Net2.0では実用上問題ないと言えなくもないですが。

> それならばformCloseチェックとform_Closingイベント処理中でロックかけたらどうでしょうか?
> フォームがクローズされた後でNotingにクリアされる前にInvokeされることが原因だと思いますので。

チェックを2重にかけてもあまり関係ありませんでした。
少なくなったような気がしなくもないですが、
統計的に有意であると言えるほどまだ試行できていません。
1週間後くらいにはわかるかと…。

追記。
読み違えてました。フラグではなくロックですね。
ロックをかけるとは、具体的にはなにをどうロックするんでしょうか?
私には具体的内容が想起できませんでした。
引用返信 編集キー/
■6947 / inTopicNo.35)  Re[8]: Control.Invokeが使えない件。
□投稿者/ れい (73回)-(2007/08/27(Mon) 20:06:39)
ダメだしばっかりで申し訳ないですが。

No6936 (困ったちゃん さん) に返信
> Closing イベントハンドラで Me.Close を BeginInvoke します。
> これなら、溜まった Invoke が片付いた後に Close が実行されます。

これも試行していますが、
やはりだめでした。

BeginInvoke内部で、
現在のスレッドでInvokeが必要かどうか、
つまり現在のスレッドのIDと、Windowを作ったスレッドのIDが同じであるか確認しています。
Invokeが必要ない場合はそのスレッドでそのままメソッドを呼びますので、
CloseやClosingでBeginInvokeしても、普通に呼ぶのと変わりありません。

Invokeしようとしている時、Invokeしてる最中に、
Formが「絶対に」閉じないようにする方法がないのでどうにもできない…。
引用返信 編集キー/
■6951 / inTopicNo.36)  Re[8]: Control.Invokeが使えない件。
□投稿者/ 困ったちゃん (10回)-(2007/08/27(Mon) 20:45:35)
No6947 (れい さん) に返信
> Invokeが必要ない場合はそのスレッドでそのままメソッドを呼びますので、
> CloseやClosingでBeginInvokeしても、普通に呼ぶのと変わりありません。

スレッドはその通りなんですが、
BeginInvoke 経由で Me.Close() が実行に入る順番が、
formCloseチェックを潜り抜けた最後の Invoke の実行完了後に
後回しにされていると思うのですが。

確認のために Me.Close() を表示機能付きの適当な中間メソッド myClose() で受け、
Me.BeginInvoke(New System.Threading.ThreadStart(AddressOf Me.myClose))

Me.myClose()
とを比較すると、明らかに実行順序が変わります。

噛み合っていませんか?
引用返信 編集キー/
■6953 / inTopicNo.37)  Re[9]: Control.Invokeが使えない件。
□投稿者/ れい (74回)-(2007/08/27(Mon) 21:07:17)
No6951 (困ったちゃん さん) に返信
> ■No6947 (れい さん) に返信
> とを比較すると、明らかに実行順序が変わります。
> 噛み合っていませんか?

うげ。また日本語能力の無さを露呈してしまった。

はい。おっしゃるとおり。
順番は変わります。

が、やはり停止します。
BeginInvokeでCloseがスレッドのInvokeキューに入り、
後で処理されますが、
この一連の動作の最中に他のスレッドに切り替わることが保証できないので、
同様に停止します。

FormClosingイベントで確認してます。
(FormClosedと書いてありますが、間違いですよね)
引用返信 編集キー/
■6955 / inTopicNo.38)  Re[10]: Control.Invokeが使えない件。
□投稿者/ 困ったちゃん (11回)-(2007/08/27(Mon) 21:42:51)
No6953 (れい さん) に返信
> BeginInvokeでCloseがスレッドのInvokeキューに入り、
> 後で処理されますが、
> この一連の動作の最中に他のスレッドに切り替わることが保証できないので、
> 同様に停止します。

Me.Close()をBeginInvokeした時のForm2のスレッドを時系列で追うと;
Invoke
...
Form2_FormClosing
formClose = True
Invoke ' formCloseを潜り抜けたInvoke
...
Invoke ' formCloseを潜り抜けた#最後#のInvoke
Close ' Form2_FormClosingでBeginInvokeしたClose
を期待したのですが、そうなることが保障できないということでしょうか。
それとも、私が論点を全く取り違えているのでしょうか???

> FormClosingイベントで確認してます。
> (FormClosedと書いてありますが、間違いですよね)
はい。うっかりコピペしちゃいました。すみません。
引用返信 編集キー/
■6957 / inTopicNo.39)  Re[11]: Control.Invokeが使えない件。
□投稿者/ れい (75回)-(2007/08/27(Mon) 22:28:41)
No6955 (困ったちゃん さん) に返信
> Me.Close()をBeginInvokeした時のForm2のスレッドを時系列で追うと;
> Invoke
> ...
> Form2_FormClosing
> formClose = True
> Invoke ' formCloseを潜り抜けたInvoke
> ...
> Invoke ' formCloseを潜り抜けた#最後#のInvoke
> Close ' Form2_FormClosingでBeginInvokeしたClose
> を期待したのですが、そうなることが保障できないということでしょうか。

はい。
よく考えればわかるかと思いますが、保証できません。
formCloseチェックを潜り抜けてから、Control.Invokeを呼ぶまで、
他のスレッドの処理がどのくらい進むかは、何もいえません。
ですので、やはりブロックしてフリーズします。

引用返信 編集キー/
■6959 / inTopicNo.40)  Re[12]: Control.Invokeが使えない件。
 
□投稿者/ えムナウ (86回)-(2007/08/28(Tue) 05:49:45)
2007/08/28(Tue) 05:50:27 編集(投稿者)
とりあえず。
以下のコードで私の環境では現在33128、まだ動作しています。
FormClosedでも25000以上は動いていてプログラムを終了させました。

Public Class Form1
    Inherits System.Windows.Forms.Form

    Public Form2SyncObject As New Object

    Public ChildForm As Form2
    Public InvokeIteration As Integer = 0
    Public FormCreateIteration As Integer = 0
    Public ChildFormCreated As Boolean
    Public Timer1 As System.Windows.Forms.Timer
    Private SyncObject As Object

    Private Sub Form1_Load(ByVal sender As Object, ByVal e As System.EventArgs) Handles MyBase.Load
        Me.components = New System.ComponentModel.Container
        Timer1 = New System.Windows.Forms.Timer(Me.components)
        Me.components.Add(Timer1)
        AddHandler Timer1.Tick, New EventHandler(AddressOf Timer1_Tick)
        Me.Timer1.Interval = 10
        Me.Timer1.Start()
    End Sub


    Private Sub Timer1_Tick(ByVal sender As System.Object, ByVal e As System.EventArgs)
        If ChildForm Is Nothing Then
            Dim subthread As New System.Threading.Thread(AddressOf Me.SubThreadProc)
            SyncLock Form2SyncObject
                ChildFormCreated = False
            End SyncLock
            subthread.Start()
            Me.Text = "Create"
            FormCreateIteration += 1
            InvokeIteration = 0
            While True
                SyncLock Form2SyncObject
                    If ChildFormCreated Then Exit Sub
                End SyncLock
            End While
        End If
        InvokeIteration += 1
        Me.Text = FormCreateIteration.ToString & ":" & InvokeIteration.ToString
        Try
            SyncLock Form2SyncObject
                ChildForm.WriteLine(FormCreateIteration & ":" & InvokeIteration.ToString)
            End SyncLock
        Catch ex As Exception
        End Try
    End Sub

    Private Sub SubThreadProc()
        ChildForm = New Form2
        ChildForm.MainForm = Me
        ChildForm.ShowDialog()
        SyncLock Form2SyncObject
            ChildForm.Dispose()
            ChildForm = Nothing
        End SyncLock
    End Sub
End Class

Public Class Form2
    Inherits System.Windows.Forms.Form

    Private Delegate Sub WriteLineDelegate(ByVal text As String)
    Public MainForm As Form1
    Public TextBox1 As TextBox
    Private formClose As Boolean = False
    Private formCloseLockObject As New Object

    Public Sub WriteLine(ByVal text As String)
        Dim lockedFormClose As Boolean
        SyncLock formCloseLockObject
            lockedFormClose = formClose
        End SyncLock
        If Not lockedFormClose Then
            If Me.InvokeRequired Then
                Me.Invoke(New WriteLineDelegate(AddressOf WriteLine), New Object() {text})
            Else
                Me.Text = text
                Me.TextBox1.Text = text
                Me.TextBox1.ScrollToCaret()
                Me.TextBox1.Focus()
            End If
        End If
    End Sub

    Private Sub Form2_Load(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles MyBase.Load
        Me.components = New System.ComponentModel.Container
        Dim Timer1 As New System.Windows.Forms.Timer(Me.components)
        TextBox1 = New TextBox
        Me.components.Add(Timer1)
        Me.Controls.Add(TextBox1)
        AddHandler Timer1.Tick, New EventHandler(AddressOf Timer1_Tick)
        AddHandler Me.FormClosed, New FormClosedEventHandler(AddressOf Form2_FormClosed)
        AddHandler Me.FormClosing, New FormClosingEventHandler(AddressOf Form2_FormClosing)
        SyncLock MainForm.Form2SyncObject
            MainForm.ChildFormCreated = True
        End SyncLock
        Timer1.Interval = New Random().Next(100, 1000)
        Timer1.Start()
    End Sub

    Private Sub Timer1_Tick(ByVal sender As System.Object, ByVal e As System.EventArgs)
        Me.Close()
    End Sub

    Private Sub Form2_FormClosed(ByVal sender As System.Object, ByVal e As System.Windows.Forms.FormClosedEventArgs)
        'formClose = True
    End Sub

    Private Sub Form2_FormClosing(ByVal sender As System.Object, ByVal e As System.Windows.Forms.FormClosingEventArgs)
        SyncLock formCloseLockObject
            formClose = True
        End SyncLock
    End Sub
End Class

引用返信 編集キー/

<前の20件 | 次の20件>
トピック内ページ移動 / << 0 | 1 | 2 >>

管理者用

- Child Tree -