[英]Swapping mutex locks
我在正確地“交換”鎖時遇到了麻煩。 考慮這種情況:
bool HidDevice::wait(const std::function<bool(const Info&)>& predicate)
{
/* A method scoped lock. */
std::unique_lock waitLock(this->waitMutex, std::defer_lock);
/* A scoped, general access, lock. */
{
std::lock_guard lock(this->mutex);
bool exitEarly = false;
/* do some checks... */
if (exitEarly)
return false;
/* Only one thread at a time can execute this method, however
other threads can execute other methods or abort this one. Thus,
general access mutex "this->mutex" should be unlocked (to allow threads
to call other methods) while at the same time, "this->waitMutex" should
be locked to prevent multiple executions of code below. */
waitLock.lock(); // How do I release "this->mutex" here?
}
/* do some stuff... */
/* The main problem is with this event based OS function. It can
only be called once with the data I provide, therefore I need to
have a 2 locks - one blocks multiple method calls (the usual stuff)
and "waitLock" makes sure that only one instance of "osBlockingFunction"
is ruinning at the time. Since this is a thread blocking function,
"this->mutex" must be unlocked at this point. */
bool result = osBlockingFunction(...);
/* In methods, such as "close", "this->waitMutex" and others are then used
to make sure that thread blocking methods have returned and I can safely
modify related data. */
/* do some more stuff... */
return result;
}
我如何解決這個“交換”問題而又不使代碼過於復雜? 我可以先鎖定this->mutex
然后再鎖定另一個,但是我擔心在那一納秒內會發生競爭。
編輯:
想象一下,有3個線程正在調用wait
方法。 第一個將鎖定this->mutex
,然后鎖定this->mutex
this->waitMutex
,然后將其解鎖this->mutex
。 第二個將鎖定this->mutex
並且必須等待this->waitMutex
可用。 它不會解鎖this->mutex
。 第三個將卡在鎖定this->mutex
。
我想讓最后2個線程等待this->waitMutex
可用。
編輯2:
擴展了osBlockingFunction
示例。
聞起來好像與HidDevice::wait
上的std::condition_variable cv
的設計/實現應該有點不同,並且只有一個互斥體。 當您編寫“其他線程可以執行其他方法或中止該方法”時,將調用cv.notify_one
來“中止”該等待。 cv.wait
{自動輸入等待並解鎖互斥鎖},而在cv.notify
{自動cv.notify
等待並鎖定互斥鎖}。 像那HidDevice::wait
更簡單:
bool HidDevice::wait(const std::function<bool(const Info&)>& predicate)
{
std::unique_lock<std::mutex> lock(this->m_Mutex); // Only one mutex.
m_bEarlyExit = false;
this->cv.wait(lock, spurious wake-up check);
if (m_bEarlyExit) // A bool data-member for abort.
return;
/* do some stuff... */
}
/* do some checks... */
線程等待直到某些邏輯變為真。 “中止”等待,將由另一個線程調用其他HidDevice
函數的責任:
void HidDevice::do_some_checks() /* do some checks... */
{
if ( some checks )
{
if ( other checks )
m_bEarlyExit = true;
this->cv.notify_one();
}
}
類似的東西。
我建議創建一個小的“解鎖”工具。 這是具有反向語義的互斥包裝。 在lock
它會unlocks
,反之亦然:
template <class Lock>
class unlocker
{
Lock& locked_;
public:
unlocker(Lock& lk) : locked_{lk} {}
void lock() {locked_.unlock();}
bool try_lock() {locked_.unlock(); return true;}
void unlock() {locked_.lock();}
};
現在代替:
waitLock.lock(); // How do I release "this->mutex" here?
您可以說:
unlocker temp{lock};
std::lock(waitLock, temp);
其中lock
是unique_lock
而不是持有mutex
的lock_guard
。
這將鎖定waitLock
並解鎖mutex
,就像通過一條不間斷的指令一樣。
現在,在對所有這些進行編碼之后,我可以推斷它可以轉換為:
waitLock.lock();
lock.unlock(); // lock must be a unique_lock to do this
第一版本的可讀性或高或低是一個問題。 第一個版本更容易推論(一旦知道std::lock
作用)。 但是第二個更簡單。 但是第二點,讀者必須更仔細地考慮正確性。
更新
只需閱讀問題中的編輯內容即可。 此解決方案不能解決編輯中的問題:第二個線程將阻止第三個(及后續線程)在需要mutex
但不需要waitMutex
任何代碼中取得進展,直到第一個線程釋放waitMutex
。
因此,從這個意義上講,我的回答在技術上是正確的,但不滿足所需的性能特征。 我將其留作參考。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.