1

ダイアログまたはその他のユーザーインタラクションをポップする必要があるバックグラウンドスレッドで実行されているコードがあるためInvoke、UIスレッドへの通常の呼び出しを行います。

Control.Invoke(SomeFunction);

void SomeFunction()
{
  ...
}

しかし、バグに遭遇しました。UIスレッドが呼び出しにすぐに応答しないことがありInvokeます。UIスレッドが現在、まだ返されていないクロスプロセスDCOM呼び出しを実行していたという事実まで追跡しました。DCOM呼び出しが返されると、関数が呼び出されますが、それまではInvoke呼び出しがハングしたように見えました。

これに対する私の解決策は、タイムアウトを導入することでした:

ManualResetEvent invokeEvent = new ManualResetEvent();
var result = Control.BeginInvoke(SomeFunction, invokeEvent);

if (!invokeEvent.WaitOne(1000))
  throw new Exception("Not responding");

Control.EndInvoke(result);

void SomeFunction(ManualResetEvent invokeEvent)
{
  invokeEvent.Set();

  ...
}

これは「私のマシン感覚で動作する」で機能しましたが、いくつかの欠陥がありました。


(出典:codinghorror.com

  • まず、タイムアウトが発生した場合でも、関数は呼び出されます-DCOM呼び出しが実際に完全にハングしていなかった場合、最終的に実行されます
  • 第二に、明らかな恐ろしい競合状態があります
  • 最後に、全体の「Arrgh」性があります

最初の2つのことが解決できたとしても、私たちはまだ一般的な不快感を持っています。これを解決するためのより良い方法はありますか?

4

2 に答える 2

0

クロスプロセスDCOM呼び出しを別のスレッドに移動します。あなたは明らかにUIスレッドをぶら下げていますが、これは完全に受け入れられません。これを修正すると、ファントムの問題(OP)もなくなります。

于 2009-07-20T08:24:38.717 に答える
0

これは、GUIスレッドで何かを実行する場合によくあるスレッドの問題であり、この症状はすべての種類の開発者に影響します。

実際の進行状況ダイアログを表示する別のスレッドと、DCOM呼び出しを実行する別のスレッドを作成する場合は、2つのスレッド間でManuaResetEvent同期を移動するだけです。これには、進行状況フォームを作成する別のスレッドに独自のメッセージキューが作成され、DCOM呼び出しの実行に使用される2番目のスレッドがGUIスレッドをロックする必要がないため、GUIスレッドをロックしないという利点があります。

慎重に同期する必要がありますが、一度実行すると、実際に動作するのを見るのは美しいです。

private ManualResetEvent _event = new ManualResetEvent(false);
...

private void StartTheComProgressCall()
{
    _event.Reset();

    ThreadPool.QueueUserWorkItem(StartProgressDialog);
    ThreadPool.QueueUserWorkItem(StartDCOMCall);

    // there's various possibilities to perform here, we could ideally 1) wait on the
    // event to complete, 2) run a callback delegate once everything is done
    // 3) fire an event once completed
}

private void StartProgressDialog(object state)
{
    ProgressDialog dialog = new ProgressDialog();
    dialog.Show();

    while(!_event.WaitOne(0))
        Application.DoEvents();

    dialog.Close();
}

private void StartDCOMCall()
{
    ...
   <perform your DCOM routines here>

    // once the call is done, remember to trigger that it's complete
    // so that blocking threads can continue to do what they need to do
    _event.Set();
}

Application.DoEvents()このメソッド の使用に反対する人もいるかもしれませんがDoEvents、呼び出しはGUIではなく別のスレッド(進行状況ダイアログを作成したスレッド)で行われるため、現在の呼び出しスレッドのメッセージキューにある保留中のWindowsメッセージを強制的に処理することを検討してください。スレッド、それを使用することでこれ以上または倫理的な「コードの臭い」の問題はないはずです。私たちは仕事を成し遂げるのに役立つどんなツールやテクニックも使うべきです。

于 2009-07-20T09:58:44.250 に答える