如何正确离开临界区?
How to properly leave a Critical Section?
我有以下 C++ 代码,其中我使用了 Critical Section object:
EnterCriticalSection(&cs);
// code that may throw an exception
LeaveCriticalSection(&cs);
如何确保即使抛出异常也会调用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,那么 类 抽象了这样的东西:is Ccriticalsection usable in production?
"How can I ensure that the LeaveCriticalSection function is called even if an exception is thrown?"
你可以这样写一个小帮手class:
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. When you use it you also can use RAII idiom wrapper for auto unlocking mutex (std::lock_guard ).
获得相同的结果
更新:临界区和互斥量之间的一个区别是您可以在一个线程上多次锁定临界区,但对于简单的 std::mutex 而言并非如此。如果您需要锁定的递归行为,请使用 std::recursive_mutex std::lock_guard<std::recursive_mutex>
更新 2:描述了关键部分和互斥锁之间的详细区别 here, performance comparison is here。
理由:尽可能使用标准定义的机制。如果您使用特定于平台的东西 - 将其包裹起来。因此,如果您担心性能 - 使用 lock/unlock 方法创建关键部分 class(以满足 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
我有以下 C++ 代码,其中我使用了 Critical Section object:
EnterCriticalSection(&cs);
// code that may throw an exception
LeaveCriticalSection(&cs);
如何确保即使抛出异常也会调用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,那么 类 抽象了这样的东西:is Ccriticalsection usable in production?
"How can I ensure that the LeaveCriticalSection function is called even if an exception is thrown?"
你可以这样写一个小帮手class:
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. When you use it you also can use RAII idiom wrapper for auto unlocking mutex (std::lock_guard ).
获得相同的结果更新:临界区和互斥量之间的一个区别是您可以在一个线程上多次锁定临界区,但对于简单的 std::mutex 而言并非如此。如果您需要锁定的递归行为,请使用 std::recursive_mutex std::lock_guard<std::recursive_mutex>
更新 2:描述了关键部分和互斥锁之间的详细区别 here, performance comparison is here。
理由:尽可能使用标准定义的机制。如果您使用特定于平台的东西 - 将其包裹起来。因此,如果您担心性能 - 使用 lock/unlock 方法创建关键部分 class(以满足 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