我对在多线程应用程序中正确使用关键部分感到困惑。在我的应用程序中,有几个在线程之间共享的对象(一些循环缓冲区和一个串行端口对象)。是否应将这些对象的访问权限置于关键部分内,或仅在特定时间进行?我怀疑只是在某些时候,因为当我试图用EnterCriticalSection
/ LeaveCriticalSection
包裹每次使用时,我遇到了似乎是死锁的情况。您可能有任何见解将不胜感激。感谢。
答案 0 :(得分:6)
如果您跨线程共享资源,并且其他一些线程在其他线程写入时读取,那么它必须始终受到保护。
如果不了解更多关于代码的内容,很难提供更多建议,但这里有一些要记住的一般要点。
1)关键部分保护资源,而不是进程。
2)在所有线程中以相同的顺序输入/离开关键部分。如果线程A进入Foo,然后输入Bar,则线程B必须以相同的顺序输入Foo和Bar。如果不这样做,你可以创建一个种族。
3)必须以相反的顺序进入和离开。例如,由于您输入了Foo然后进入了Bar,您必须在离开Foo之前离开Bar。如果你不这样做,你可能会造成死锁。
4)合理地在最短的时间内保持锁定。如果你在开始使用Bar之前已经完成了Foo,那么在抓住Bar之前释放Foo。但是你仍然必须从上面牢记订购规则。在同时使用Foo和Bar的每个线程中,您必须以相同的顺序获取和释放:
Enter Foo
Use Foo
Leave Foo
Enter Bar
Use Bar
Leave Bar
5)如果你只读了99.9%的时间并写了0.1%的时间,不要试图聪明。即使你只是在阅读,你仍然必须进入暴击秒。这是因为当你在阅读过程中时,你不希望写入开始。
6)保持关键部分的细化。每个关键部分应该保护一个资源,而不是多个资源。如果你使关键部分太“大”,你可以序列化你的应用程序或创建一组非常神秘的死锁或种族。
答案 1 :(得分:2)
在支持RAII的关键部分周围使用C ++包装器:
{
CriticalSectionLock lock ( mutex_ );
Do stuff...
}
锁的构造函数获取互斥锁,即使抛出异常,析构函数也会释放互斥锁。
尽量不要一次获得超过一个锁,并尽量避免在持有锁时调用类外的函数;这有助于避免在不同的地方获得锁定,因此您可以减少死锁的可能性。
如果您必须同时获得多个锁,请按地址对锁进行排序并按顺序获取。这样,多个进程在没有协调的情况下以相同的顺序获得相同的锁。
使用IO端口,考虑是否需要在输入的同时锁定输出 - 通常情况下,某些东西会尝试写入,然后期望读取,反之亦然。如果你有两个锁,那么你可以在一个线程写入然后读取时获得死锁,然后另一个读取然后写入。通常有一个执行IO和请求队列的线程解决了这个问题,但这比仅使用锁包装调用要复杂一点,而且没有更多细节我不推荐它。