2016-12-06 9 views
0

GUIレイヤーをサポートする必要があったコンソールサーバーアプリケーションがあります。私はguiフォームを含むクラスライブラリを作成し、コンソールアプリケーションプロジェクトから参照し、フォームをインスタンス化して別のスレッドで実行させました。したがって、サーバーオブジェクトとフォームオブジェクトは異なるスレッドに存在します。UIスレッドが他のスレッドから呼び出されたイベントをリッスンするときのスレッドの安全性

サーバーはゲームサーバーです。現在マップに配置されているゲームオブジェクトのコレクションを維持します。もちろん、時間が経つにつれて成長したり縮小したりします。このフォームでは、これらのオブジェクトをすべてリアルタイムで視覚化する必要がありました。当然のことながら、フォームのためにオブジェクトのリストに変更があったときにサーバーが呼び出すイベントを追加しました。

class Server { 
    event Action<IEnumerable> ObjectListChanged: 
} 

フォームがサーバーに接続されると、ハンドラーが追加されます。

Server server = new Server(); 
var form = new VisializerForm(); 
server.ObjectListChanged += form.OnObjectListChanged; 

フォームを閉じると、サーバーからハンドラーが削除されます。イベントの呼び出しがサーバースレッドで発生したよう

form.Closing += (s,e) => { 
    server -= form.OnObjectListChanged: 
}; 

、OnObjectListChangedの実装は、InvokeRequiredプロパティのチェックが含まれています。

class VisualizerForm : Form { 
    void OnObjectListChanged(IEnumerable objects){ 
     if(!Visible || IsDisposed || !IsHandleCreated) return; 

     if(InvokeRequired) { 
      Invoke ((Action)(()=>OnObjectListChanged (objects))); 
      return; 
     } 

     // .... 
    } 
} 

これは、フォームを閉じる時点までうまくいきます。 Form.Closingイベントでイベントハンドラが削除され、フォームオブジェクトが閉じられた後にイベントが呼び出されると例外がスローされます。私はデバッガでチェックすると、イベントには明らかにハンドラがありません(ビジュアルスタジオのウォッチウィンドウに表示されている場合はnullを表示します)。スレッドは、何があってもイベントハンドラ内でForm.Invokeの呼び出しに達しました。ほとんどの場合、ある時点でマルチスレッドを台無しにしてしまった可能性があります。私は間違って何をしていますか?ここで何が起こっているのでしょうか?

申し訳ありませんが、私は携帯電話でそれを慣れていない、それに慣れていないフォーマット。


以下のようにコードを変更しても、同じ例外が発生します。

public void OnObjectListChanged(IEnumerable objects) 
    { 
     if (IsDisposed || Disposing || !Visible || !IsHandleCreated) return; 

     if (InvokeRequired) { 
      try { 
       Invoke((Action)(() => OnFieldObjectListChanged(zoneId, channel, removed, added))); 
      } catch (Exception e) { 
       Debugger.Break(); // <-- server thread enters here. 
      } 
      return; 
     } 

     // .... 
    } 

そして次は私がそのcatch節で取得した例外とスタックトレースです:一方

위치: System.Windows.Forms.Control.MarshaledInvoke(Control caller, Delegate method, Object[] args, Boolean synchronous) 
    위치: System.Windows.Forms.Control.Invoke(Delegate method, Object[] args) 
    위치: System.Windows.Forms.Control.Invoke(Delegate method) 
    위치: VisualZoneServerLib.Visualization.VisualZoneServerWindow.OnFieldObjectListChanged(Int32 zoneId, Int32 channel, IList`1 removed, IList`1 added) 파일 D:\Server\ZoneServerLib.Visualizer\Visualization\VisualZoneServerWindow.cs:줄 310 

、UIスレッドには、Disposeメソッドのオーバーライドで停止しました。

protected override void Dispose(bool disposing) 
    { 
     if (disposing) { 
      components?.Dispose(); 
      m_timeLineWindow?.Dispose(); 
      m_iesSnapshots?.Dispose(); 
     } 
     base.Dispose(disposing); // <- UI thread stopped here 
    } 

'' '処分' を呼び出し、再びキーワード 'Winフォーム' を検索してthis threadを発見しました。それは私がやっているのと同じ問題について話しているようです。

+0

問題を再現するのに十分な例はまだありません。 – Servy

+0

その例外のスタックトレースを提供できますか? –

+0

今はできません。私はアクセスしたときにスタックトレースを追加します。 – user2883715

答えて

2

これまで同様の問題に遭遇しましたが、私が持っていた問題は、Invoke ((Action)(()=>OnObjectListChanged (objects)));が要求を処理するためにメッセージポンプの待ち行列を待たなければならないことでした。フォームが閉じられ、廃棄されても、そのアイテムが処理されるとキューに保留中の依頼が残っていれば、そのフォームにアクセスします。

確認したことの順序を変更して、IsDisposedが最初にチェックされるようにしてください。また、Disposingもそこにチェックインしてください。

void OnObjectListChanged(IEnumerable objects){ 
    if(IsDisposed || Disposing || !Visible || !IsHandleCreated) return; 

    if(InvokeRequired) { 
     Invoke ((Action)(()=>OnObjectListChanged (objects))); 
     return; 
    } 

    // .... 
} 
+0

ありがとうございます。後でそれを試し、何が起こるか教えてくれます。 – user2883715

+0

@ user2883715更新を見るには、「Disposing」もチェックしてください。 –

+0

質問が更新されました。 – user2883715

関連する問題