如何正确地离开关键部分?

我有以下C ++代码,我使用临界区对象 :

EnterCriticalSection(&cs); // code that may throw an exception LeaveCriticalSection(&cs); 

如何确保即使抛出exception也会调用LeaveCriticalSection函数?

只要写一个使用析构函数进行清理的警卫:

 struct Guard { CriticalSection& cs; Guard(CriticalSection& cs) : cs(cs) { EnterCriticalSection(cs); } ~Guard() { LeaveCriticalSection(cs); } Guard(const Guard&) = delete; Guard& operator = (const Guard&) = delete; }; 

用法:

 void f() { Guard guard(cs); ... } 

使用RAII(资源获取初始化)习语:

 struct GuardCS { GuardCS(CRITICAL_SECTION& p_cs) : cs(p_cs){ EnterCriticalSection(&cs); } ~GuardCS() { LeaveCriticalSection(&cs); } private: // Protect against copying, remove: =delete on pre c++11 compilers GuardCS(GuardCS const &) = delete; GuardCS& operator =(GuardCS const &) = delete; CRITICAL_SECTION& cs; }; 

如果你有任何机会使用MFC,那么有些类可以抽象出这样的东西: Ccriticalsection是否可用于生产?

“如何确保即使抛出异常也能调用LeaveCriticalSection函数?

你可以写一个这样的小助手类:

  class CsLocker { public: CsLocker(CriticalSection& cs) : cs_(cs) { EnterCriticalSection(&cs_); } ~CsLocker() { LeaveCriticalSection(&cs); } CsLocker(const CsLocker&) = delete; CsLocker& operator=(const CsLocker&) = delete; private: CriticalSection& cs_; }; 

这将保证关键部分在任何时候(以及为什么)保留范围时被解锁。

我建议你不要使用WinAPI关键部分。 你可以通过使用std :: mutex来得到相同的结果。 当你使用它时,你也可以使用RAII成语包装来自动解锁互斥( std :: lock_guard )。

更新:关键部分和互斥体之间的一个区别是,您可以在一个线程上多次锁定关键部分,但对于简单的std :: mutex来说这不是真的。 如果你需要递归锁定的行为,使用std :: recursive_mutex std::lock_guard<std::recursive_mutex>

更新2:在这里描述关键部分和互斥体之间的细节差异,性能比较在这里 。

理由:只要有可能,最好使用标准定义的机制。 如果你使用平台特定的东西 – 环绕它。 所以,如果你害怕性能 – 使用锁定/解锁方法创建临界区类(以满足BasicLocakable概念要求)并使用std::lock_guard<MyCriticalSection>

其他答案对于使用RAII对象是正确的,但我觉得值得指出一个简单的方法来做到这一点与Boost.ScopeExit

 #include <boost/scope_exit.hpp> ... EnterCriticalSection(&cs); BOOST_SCOPE_EXIT(&cs) { LeaveCriticalSection(&cs); } BOOST_SCOPE_EXIT_END // code that may throw an exception