-3

古い管理されていない C++ の時代に戻ると、マルチスレッド アプリケーションのクリティカル セクションを信頼できました。そのため、現在 dotNet/C# を使用して、ロック メカニズムを中継していました。リソースをロックすることで、どのスレッドもコード内のそれらのリソースにアクセスできないと確信しました。

これは dotNet では当てはまらないようです!

Windows サービス アプリケーションがあります。サード パリティ OCX をホストする非表示のフォームを使用して、メイン マネージ スレッドを作成します。このスレッド内で、オブジェクトのリストに対するメッセージ ポンピングとポーリングを行います。このオブジェクトのリストは、このマネージ スレッド内の OCX によって起動されたイベントによって変更されます。

コードの簡略化された部分をここに投稿します。

  public bool Start()
  {
     ServiceIsRunning = true;

     m_TaskThread = new Thread(new ParameterizedThreadStart(TaskLoop));
     m_TaskThread.SetApartmentState(ApartmentState.STA);
     m_TaskThread.Start(this);

     return true;
  }

  private void OnOCXEvent(object objToAdd)
  {
     lock(m_ObjectList)
     {
        m_ObjectList.Add(objToAdd);            }
     }
  }

  private void CheckList()
  {
     lock(m_ObjectList)
     {
        foreach(object obj in m_ObjectList)
        {
           ...
        }
     }
  }

  [STAThread] // OCX requirement!
  private void TaskLoop(object startParam)
  {
     try {

        ... 

        while (ServiceIsRunning)
        {
           // Message pump 
           Application.DoEvents();

           if (checkTimeout.IsElapsed(true))
           {
              CheckList();
           }

           // Relax process CPU time!
           Thread.Sleep(10);
        }
     } catch(Exception ex) {
        ... 
     }
  }

あなたは私を信じません: CheckList で「リストが変更されました」という例外が発生しました! 8-/

そのため、いくつかのログを記録したところ、同じマネージ スレッドが CheckList foreach ループ内にあるときに OnOCXEvent が発生することに気付きまし。確かに、ログ ファイルで同じマネージド スレッド ID を取得しました。

今、私は疑問に思っています:どうしてこれが起こるのでしょうか?より多くの win32 スレッドで単一のマネージ スレッドが実装されていますか?

なぜこれが起こっているのか誰かが説明してくれることを願っています。それで、この問題を解決できます。

ありがとう、ファビオ

私のメモ:

foreachループの前にリストのコピーを作成する問題を実際に解決しました。しかし、私はこの解決策が好きではありません。また、何が起こっているのかを理解するのも好きです。私は 3 番目のパリティ OCX コードを所有していませんが、CheckList ループ内で呼び出すメソッドは、発生した OCX イベントとは論理的に関係ありません。

4

1 に答える 1

6

これは単なる再入可能性の問題であると強く思います。

呼び出し内でCheckList、OCX メソッドを呼び出しています。それ自体がOCXイベントを発生させる可能性がある場合 (効果的な呼び出しを含むApplication.DoEvents) 、実行OnOCXEventのスレッドで呼び出されることになり、問題が発生します。CheckList

これは問題ではなく、lock再入可能性の問題です。

これを診断する 1 つの方法は、メソッドCheckListOnOCXEventメソッドを変更することです。

private bool inCheckList;

private void OnOCXEvent(object objToAdd)
{
   lock(m_ObjectList)
   {
      if (inCheckList)
      {
         throw new Exception("Look at this stack trace!");
      }
      m_ObjectList.Add(objToAdd);
   }
}

private void CheckList()
{
   lock(m_ObjectList)
   {
      inCheckList = true;
      foreach(object obj in m_ObjectList)
      {
         ...
      }
      inCheckList = false; // Put this in a finally block if you really want
   }
}

CheckList, -を含むスタック トレースで例外がスローさOnOCXEventれ、途中でメッセージ ループを実行する何かが間にあると強く思われます。

于 2013-07-26T08:50:20.030 に答える