我有以下C ++代码,我使用Critical Section object:
EnterCriticalSection(&cs);
// code that may throw an exception
LeaveCriticalSection(&cs);
即使抛出异常,如何确保调用LeaveCriticalSection
函数?
答案 0 :(得分:9)
只需使用析构函数编写一个警卫进行清理:
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);
...
}
答案 1 :(得分:8)
使用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,那么有些类可以抽象出这些内容:is Ccriticalsection usable in production?
答案 2 :(得分:4)
“即使抛出异常,如何确保调用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_;
};
这将保证关键部分在任何时候(以及为什么有)范围被解锁时都会被解锁。
答案 3 :(得分:4)
我建议你不要使用WinAPI关键部分。您可以使用std::mutex获得相同的结果。使用它时,您还可以使用RAII idiom包装器自动解锁互斥锁(std::lock_guard)。
更新:关键部分和互斥锁之间的一个区别是,您可以在一个线程上多次锁定临界区,但对于简单的std :: mutex则不然。如果您需要锁定的递归行为,请使用std::recursive_mutex std::lock_guard<std::recursive_mutex>
更新2:关键部分和互斥锁之间的详细差异被描述为here,性能比较为here。
理由:最好尽可能使用标准定义的机制。如果您使用特定于平台的东西 - 将其包裹起来。因此,如果您害怕性能 - 使用锁定/解锁方法创建关键部分类(以满足BasicLocakable概念要求)并使用std::lock_guard<MyCriticalSection>
。
答案 4 :(得分:1)
关于使用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