c#/ c ++ cli:对Array.Resize使用监视器锁

时间:2014-12-05 16:16:07

标签: c# arrays multithreading locking c++-cli

我在类的各种函数中使用数组m_aoAlternatingJobs。这些函数由不同的线程访问。因此,我在每个在实际访问之前访问此数组的函数中使用Monitor-lock(c ++ cli)。

现在的问题是,在一个函数中我正在调整数组的大小。正如MSDN文档所描述的,这将创建一个新的数组和复制值,所以即使我没有使用Monitor(我在每个必要的地方都在做),我也不会在任何情况下遇到程序崩溃,但是其他线程可以访问旧数组(另请参见此处:stackoverflow: Is Array.Resize(..) threadsafe?)。

如果我直接在数组对象上使用Monitor,则监视器无法以SynchronizationLockException退出,因为在新的未锁定对象上调用了Exit。但我必须使用锁来避免在使用另一个函数时调整数组大小。因此,我正在做以下技巧,以避免在调整大小时访问数组:

void Class1::JobsCount::set (int i_iJobCount)
{
  if (i_iJobCount < 1) i_iJobCount = 1;
  bool bSuccess = false;

  Object^ oRefOld;
  try
  {
    bSuccess = Monitor::TryEnter (m_aoJobs, msc_iMonitorTimeout);
    if (!bSuccess) return;

    oRefOld = m_aoAlternatingJobs;
    Array::Resize (m_aoJobs, i_iJobCount);
  }
  finally
  { if (bSuccess) Monitor::Exit (oRefOld); }
}

我不再获得异常,但更重要的问题是:在调整大小时,这是否会有效阻止对数组的访问?

编辑:
我知道使用单独的锁对象的可能性,感谢您的建议。不过,我想收到一个关于上述问题的答案。

3 个答案:

答案 0 :(得分:1)

如果我错了,请纠正我,但我认为你误解了Monitor :: Lock的作用。即使你调用Monitor :: Lock(m_aoJobs),它也不会阻止来自在访问数组之前不调用Monitor :: Lock的其他代码的并发访问。

解决方案很简单:有一个虚拟对象,它只用作Monitor :: Lock的参数。即使调整了数组大小,该对象仍将继续存在,并且不会导致Monitor :: Exit崩溃。然后让所有访问数组的代码首先使用相同的虚拟对象调用Monitor :: Lock,你就可以了。

try {
    Monitor::Lock(m_lock);
    Array::Resize (m_aoJobs, i_iJobCount);
}
finally { Monitor::Exit(m_lock); }

m_lock是应该在类的构造函数中创建的虚拟对象。

m_lock = gcnew Object();

答案 1 :(得分:0)

您带来的技巧可能会阻止代码的不同部分之间的并发访问,但它不会阻止Array :: Resize与代码的另一部分同时使用数组。这完全取决于Array :: Resize的实现方式。如果方法在返回之前设置m_aoJobs的新值,一旦数组的元素都被复制了,那么它就可以工作了。这可能是什么。但是,由于它是一个实现细节(除非它是Array :: Resize规范的一部分),我不会指望这种行为;它可能在未来发生变化。

答案 2 :(得分:0)

没有。这种方法不是线程安全的。看看这个场景:

  1. 线程1中的函数Class1::JobsCount::set锁定m_aoJobs

  2. 线程2中的函数1尝试锁定m_aoJobs,但无法执行此操作并等待

  3. 线程1中的函数Class1::JobsCount::set将m_aoJobs替换为新的m_aoJobs并解锁旧的m_aoJobs

  4. 线程3中的功能2锁定新的m_aoJobs

  5. 线程2中的功能1锁定旧的m_aoJobs并修改新的!

  6. 这种情况不太可能,但可能。