回到我原来的非托管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中得到了一个'list has modified >>例外! 8 - /
所以我做了一些日志记录,我注意到当 SAME 托管线程在CheckList foreach循环中时,将引发OnOCXEvent。我敢肯定:我的日志文件中有相同的托管线程ID,foreach循环没有完成,并且OnOCXEvent已被同一个管理线程调用!
现在我想知道:这怎么可能发生?使用更多win32线程实现单个托管线程吗?
希望有人能解释为什么会这样,所以我可以解决这个问题。
谢谢, 法比奥
我实际上解决了在foreach循环之前创建列表副本的问题。但我不喜欢这个解决方案。我也想了解发生了什么。我没有第三个奇偶校验OCX代码,但我在CheckList循环中调用的方法在逻辑上与OCX事件没有任何关系。
答案 0 :(得分:6)
我强烈怀疑这只是一个重新入侵的问题。
在您的CheckList
电话中,您正在调用OCX方法。如果这样做任何本身可以引发OCX事件 - 包括有效地调用Application.DoEvents
- 那么你最终可以在一个也是OnOCXEvent / em>执行CheckList
...这将导致问题。
这不是lock
的问题 - 这是重新入侵的问题。
诊断此问题的一种方法是修改CheckList
和OnOCXEvent
方法:
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
的堆栈跟踪引发的异常 - 以及介于其间的一堆代码,其中包含在中间运行消息循环的内容