-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-/

所以我做了一些日志记录,我注意到当SAME托管线程位于 CheckList foreach 循环中时,将引发 OnOCXEvent。我确定:我的日志文件中有相同的托管线程 ID,foreach 循环尚未完成,并且 OnOCXEvent 已由同一个托管线程调用!

现在我想知道:这怎么会发生?单个托管线程是否使用更多 win32 线程实现?

希望有人能解释为什么会这样,所以我可以解决这个问题。

谢谢,法比奥

我的笔记:

我实际上解决了在 foreach 循环之前创建列表副本的问题。但我不喜欢这个解决方案。我也想了解正在发生的事情。我不拥有第三奇偶校验 OCX 代码,但我在 CheckList 循环中调用的方法在逻辑上与被触发的 OCX 事件无关。

4

1 回答 1

6

我强烈怀疑这只是一个重入问题。

在您的CheckList通话中,您正在调用 OCX 方法。如果这样做本身会引发 OCX 事件——包括有效调用Application.DoEvents——那么你最终可能会在一个也在执行OnOCXEvent的线程中被调用......这将导致问题。CheckList

这不是问题lock- 这是重新进入的问题。

诊断此问题的一种方法是修改您的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 回答