我在类的各种函数中使用数组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); }
}
我不再获得异常,但更重要的问题是:在调整大小时,这是否会有效阻止对数组的访问?
编辑:
我知道使用单独的锁对象的可能性,感谢您的建议。不过,我想收到一个关于上述问题的答案。
答案 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中的函数Class1::JobsCount::set
锁定m_aoJobs
线程2中的函数1尝试锁定m_aoJobs,但无法执行此操作并等待
线程1中的函数Class1::JobsCount::set
将m_aoJobs替换为新的m_aoJobs并解锁旧的m_aoJobs
线程3中的功能2锁定新的m_aoJobs
线程2中的功能1锁定旧的m_aoJobs并修改新的!
这种情况不太可能,但可能。