0x80010100:System Callは失敗しました。例外、ContextSwitchDeadLock
-
28-10-2019 - |
質問
短い話:com inproc-server(dll)で動作するC#アプリケーションでは、「0x80010100:システムコールが失敗した」例外に遭遇し、デバッグモードではContextSwitchDeadLock例外も発生します。
詳細については、詳細:
1)C#アプリはSTAを初期化し、COMオブジェクト(「アパート」として登録)を作成します。次に、接続点を購読し、オブジェクトの使用を開始します。
2)ある段階で、COMオブジェクトは多くのイベントを生成し、同じアパートで作成されたCOMオブジェクトの非常に大きなコレクションを議論として渡します。
3)C#サイドのイベントハンドラーは上記のコレクションを処理し、時々オブジェクトのいくつかの方法を呼び出します。ある段階では、上記の例外を除いて、後者の呼び出しが失敗し始めます。
com側では、アパートは、Winprocがこのように見える隠された窓を使用します。
typedef std::function<void(void)> Functor;
LRESULT CALLBACK WndProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)
{
switch(msg)
{
case AM_FUNCTOR:
{
Functor *f = reinterpret_cast<Functor *>(lParam);
(*f)();
delete f;
}
break;
case WM_CLOSE:
DestroyWindow(hwnd);
break;
default:
return DefWindowProc(hwnd, msg, wParam, lParam);
}
return 0;
}
イベントは、COMサーバーの他の部分からこのウィンドウに投稿されます。
void post(const Functor &func)
{
Functor *f = new Functor(func);
PostMessage(hWind_, AM_FUNCTOR, 0, reinterpret_cast<LPARAM>(f));
}
イベントは、実際のパラメーションに縛られた標準的なATL CP実装であり、次のようなものに要約されます。
pConnection->Invoke(id, IID_NULL, LOCALE_USER_DEFAULT, DISPATCH_METHOD, ¶ms, &varResult, NULL, NULL);
C#では、ハンドラーは次のようになります:
private void onEvent(IMyCollection objs)
{
int len = objs.Count; // usually 10000 - 25000
foreach (IMyObj obj in objs)
{
// some of the following calls fail with 0x80010100
int id = obj.id;
string name = obj.name;
// etc...
}
}
==================
それで、上記の問題は、アパートのメッセージが提供しようとしているイベントがあまりにも詰め込まれているという理由だけで起こりますか?または、メッセージループを完全にブロックして、そのような動作を引き起こす必要がありますか?
メッセージQueueには、「OneVent」コールを評価する2つのシーケンシャルイベントがあると仮定します。最初のものはC#管理コードに入ります。これは、同じアパートである管理されていないコードの再入力を試みます。通常、これは許可されており、これをたくさん行います。いつ、どのような状況で失敗する可能性がありますか?
ありがとう。
解決
これは、複数のアパートでも機能するはずです。
- ネットワークトラフィック、タイマー、投稿されたメッセージなどの外部イベントに応答するスレッドの1つだけが応答します。
- 他のスレッドのみサービスcom要求(処理中にメインスレッドに呼び戻す場合でも)。
と
- どちらのスレッドキューもいっぱいにならず、COMがスレッドと通信するのを防ぎます。
まず:一部のオブジェクトは、他のオブジェクトと同じアパートにないようです。すべてのオブジェクトがSTAで作成されていると確信していますか?
あなたが説明しているのは、古典的なデッドロックです - それぞれがもう一方を待っている2つの独立したスレッド。それは、さまざまなスレッド上のC#とcomの側面で動作するデザインで発生することが期待されることです。
もしあなたは大丈夫です 全て オブジェクトは同じスレッド上にあり、そのスレッドにある隠されたウィンドウがあるので、それを確認する必要があると思います。 (明らかに、これには、com側によって作成され、C#側に渡された他のオブジェクトが含まれます。)
デバッガーで「一時停止」を押して各スレッドにあるコードをチェックすることでこれをデバッグしてみてください(rpcrt*.dllが表示された場合、これはプロキシを見ていることを意味します)。または、c#sidesとcom側の両方のさまざまな重要なポイントとwndprocの両方のさまざまな重要なポイントから現在のスレッドIDをデバッグすることができます - それらはすべて同じでなければなりません。
第二に: 1つのスレッドのみがワークアイテムを生成し、もう1つは呼び出しに応答するホストCOMオブジェクト(つまり、タイマー、ネットワークトラフィック、投稿されたメッセージなど)をホストするcomオブジェクト以外は何もしない場合、複数のスレッドで動作する必要があります。この場合、スレッドキューがいっぱいであり、COMが通話に返信できない可能性があります。
スレッドキューを使用する代わりに、重要なセクションで保護されたDequeを使用する必要があります。
- http://msdn.microsoft.com/en-us/library/windows/desktop/ms644944(v=vs.85).aspx
メッセージキューごとに10,000件の投稿されたメッセージの制限があります。この制限は十分に大きくなければなりません。アプリケーションが制限を超えた場合、非常に多くのシステムリソースを消費しないように再設計する必要があります。