GCC 4.8.2(Linux/Debian/Sid 64ビット)-またはGCC 4.9(利用可能な場合)-C++ 11の場合-いくつかのmutex
std::mutex gmtx;
実際には、以下のstatic
メソッドとFoo
メソッドの両方を含むクラスalpha
のbeta
メンバーです。
alpha
にロックされています
void alpha(void) {
std::lock_guard<std::mutex> g(gmtx);
beta(void);
// some other work
}
beta
が実際にロックされていることをgmtx
にチェックインしたい:
void beta(void) {
assert (gmtx.is_locked());
// some real work
}
(is_locked
はassert
内でのみ呼び出されることに注意してください...これは非常に非効率的または不正確な場合さえあります)
もちろん、beta
を呼び出す他の関数があります。
void gamma(void) {
std::lock_guard<std::mutex> g(gmtx);
beta();
// some other work
}
しかしis_locked
は存在しません。..どうすれば定義できますか? (実際には、ミューテックスが同じスレッドで[間接]呼び出し元によってロックされていることを確認したい...)
(assert
でテストしたいのは、beta
が別の場所で呼び出される可能性があるためです)
try_lock
は使用できません(再帰的ミューテックスを使用しない限り)。これは、一般的なケースでは、既にロックされているミューテックスをロックするためです(呼び出し側によって同じスレッドでロックされます)。これは未定義の動作だけでなく、完全にブロックされます。 。
本当に必要な場合を除いて、再帰的なミューテックス(プレーンなミューテックスよりもコストがかかる)は避けたいです。
注意:実際のプログラムはもう少し複雑です。実際には、すべてのメソッドは、「アイテム」に関する命名の双方向関係を維持するクラスの内部にあります。したがって、そのクラスの内部には、アイテムから名前へのマップと、名前からアイテムへのマップがあります。 beta
は、実際に名前を追加する内部メソッドであり、alpha
およびgamma
は、名前でアイテムを検索または追加するメソッド、または名前でアイテムを追加するメソッドです項目。
PS:実際のプログラムはまだリリースされていませんが、 [〜#〜] melt [〜#〜] の一部になるはずです---その未来 monitor ; here (一時的な場所)からダウンロードできます(アルファ段階、非常にバグが多い)。
_std::unique_lock<L>
_には_owns_lock
_メンバー関数があります(あなたが言うように_is_locked
_に相当)。
_std::mutex gmtx;
std::unique_lock<std::mutex> glock(gmtx, std::defer_lock);
void alpha(void) {
std::lock_guard<decltype(glock)> g(glock);
beta(void);
// some other work
}
void beta(void) {
assert(glock.owns_lock()); // or just assert(glock);
// some real work
}
_
編集:このソリューションでは、すべてのロック操作は、「生の」ミューテックスglock
ではなく、unique_lock gmtx
を介して実行する必要があります。たとえば、alpha
メンバー関数は_lock_guard<unique_lock<mutex>>
_(または単にlock_guard<decltype(glock)>
)で書き換えられます。
厳密に言えば、問題はstd::mutex
のロック状態を直接確認することでした。ただし、それを新しいクラスでカプセル化することが許可されている場合、それを行うのは非常に簡単です。
class mutex :
public std::mutex
{
public:
#ifndef NDEBUG
void lock()
{
std::mutex::lock();
m_holder = std::this_thread::get_id();
}
#endif // #ifndef NDEBUG
#ifndef NDEBUG
void unlock()
{
m_holder = std::thread::id();
std::mutex::unlock();
}
#endif // #ifndef NDEBUG
#ifndef NDEBUG
/**
* @return true iff the mutex is locked by the caller of this method. */
bool locked_by_caller() const
{
return m_holder == std::this_thread::get_id();
}
#endif // #ifndef NDEBUG
private:
#ifndef NDEBUG
std::atomic<std::thread::id> m_holder;
#endif // #ifndef NDEBUG
};
次の点に注意してください。
std::mutex
に対するオーバーヘッドがゼロです。ただし、構築/破壊の場合を除きます(mutexオブジェクトの場合は問題ではありません)。m_holder
メンバーは、ミューテックスを取得してから解放するまでの間にのみアクセスされます。したがって、ミューテックス自体がm_holder
のミューテックスとして機能します。タイプstd::thread::id
についての非常に弱い想定では、locked_by_caller
は正しく機能します。std::lock_guard
)はテンプレートであるため、この新しいクラスで適切に機能します。同じスレッドで複数回ロックできるrecursive_mutex
を使用できます。注:それが私のコードである場合は、recursive_mutex
を必要としないように再構成しますが、問題は解決されます。
まあ、アサーションの費用が本当に問題でない場合は、その動作が正しく定義されていることが保証されている別のスレッドからtry_lock()
を呼び出すだけです。
void beta(void) {
assert(std::async(std::launch::async, [] { return gmtx.try_lock(); })
.get() == false &&
"error, beta called without locking gmtx");
// some real work
}
私のソリューションはシンプルです。try_lockを使用してテストし、必要に応じてロックを解除します。
std::mutex mtx;
bool is_locked() {
if (mtx.try_lock()) {
mtx.unlock();
return false;
}
return true; // locked thus try_lock failed
}