我有一個被多個執行緒使用的結構實體。每個執行緒都包含未知數量的函式呼叫,這些函式呼叫會改變結構體成員變數。
我有一個專用函式,它嘗試為當前執行緒“保留”結構實體,并且我想確保在原始執行緒允許之前沒有其他執行緒可以保留該實體。
我想到了互斥體,因為它們可用于保護資源,但我只知道 std::lock_guard 在單個函式的范圍內,但沒有為鎖定和解鎖之間的所有函式呼叫添加保護。
是否有可能保護這樣的資源,當我知道它總是按這個順序呼叫 Reserve 和 release 時?
更好地解釋它的代碼段:
#include <iostream> // std::cout
#include <thread> // std::thread
#include <mutex> // std::mutex
struct information_t {
std::mutex mtx;
int importantValue = 0;
// These should only be callable from the thread that currently holds the mutex
void incrementIt() { importantValue ; }
void decrementIt() { importantValue--; }
void reset() { importantValue = 0; }
} protectedResource; // We only have one instance of this that we need to work with
// Free the resource so other threads can reserve and use it
void release()
{
std::cout << "Result: " << protectedResource.importantValue << '\n';
protectedResource.reset();
protectedResource.mtx.unlock(); // Will this work? Can I guarantee the mtx is locked?
}
// Supposed to make sure no other thread can reserve or use it now anymore!
void reserve()
{
protectedResource.mtx.lock();
}
int main()
{
std::thread threads[3];
threads[0] = std::thread([]
{
reserve();
protectedResource.incrementIt();
protectedResource.incrementIt();
release();
});
threads[1] = std::thread([]
{
reserve();
// do nothing
release();
});
threads[2] = std::thread([]
{
reserve();
protectedResource.decrementIt();
release();
});
for (auto& th : threads) th.join();
return 0;
}
uj5u.com熱心網友回復:
我對每條評論的建議:
一個更好的習慣用法可能是一個監視器,它保持資源的鎖定并提供對所有者的訪問。要獲取資源,
reserve()可以回傳這樣的監視器物件(類似于訪問資源內容的代理)。任何競爭訪問reserve()現在都會阻塞(因為互斥鎖被鎖定)。當資源擁有執行緒完成時,它只會破壞監視器物件,從而解鎖資源。(這允許將 RAII 應用于所有這些,從而使您的代碼安全且可維護。)
我修改了 OP 代碼以勾勒出它的樣子:
#include <iostream> // std::cout
#include <thread> // std::thread
#include <mutex> // std::mutex
class information_t {
private:
std::mutex mtx;
int importantValue = 0;
public:
class Monitor {
private:
information_t& resource;
std::lock_guard<std::mutex> lock;
friend class information_t; // to allow access to constructor.
private:
Monitor(information_t& resource):
resource(resource), lock(resource.mtx)
{ }
public:
~Monitor()
{
std::cout << "Result: " << resource.importantValue << '\n';
resource.reset();
}
Monitor(const Monitor&) = delete; // copying prohibited
Monitor& operator=(const Monitor&) = delete; // copy assign prohibited
public:
// exposed resource API for monitor owner:
void incrementIt() { resource.incrementIt(); }
void decrementIt() { resource.decrementIt(); }
void reset() { resource.reset(); }
};
friend class Monitor; // to allow access to private members
public:
Monitor aquire() { return Monitor(*this); }
private:
// These should only be callable from the thread that currently holds the mutex
// Hence, they are private and accessible through a monitor instance only
void incrementIt() { importantValue ; }
void decrementIt() { importantValue--; }
void reset() { importantValue = 0; }
} protectedResource; // We only have one instance of this that we need to work with
#if 0 // OBSOLETE
// Free the resource so other threads can reserve and use it
void release()
{
protectedResource.reset();
protectedResource.mtx.unlock(); // Will this work? Can I guarantee the mtx is locked?
}
#endif // 0
// Supposed to make sure no other thread can reserve or use it now anymore!
information_t::Monitor reserve()
{
return protectedResource.aquire();
}
using MyResource = information_t::Monitor;
int main()
{
std::thread threads[3];
threads[0]
= std::thread([]
{
MyResource protectedResource = reserve();
protectedResource.incrementIt();
protectedResource.incrementIt();
// scope end releases protectedResource
});
threads[1]
= std::thread([]
{
try {
MyResource protectedResource = reserve();
throw "Haha!";
protectedResource.incrementIt();
// scope end releases protectedResource
} catch(...) { }
});
threads[2]
= std::thread([]
{
MyResource protectedResource = reserve();
protectedResource.decrementIt();
// scope end releases protectedResource
});
for (auto& th : threads) th.join();
return 0;
}
輸出:
Result: 2
Result: -1
Result: 0
在coliru上進行現場演示
是否有可能保護這樣的資源,當我知道它總是按這個順序呼叫 Reserve 和 release 時?
沒有必要再擔心這個了。正確的用法是燒錄:
- 要訪問資源,您需要一個監視器。
- 如果您獲得它,您就是該資源的唯一所有者。
- 如果退出范圍(將監視器存盤為區域變數的位置),監視器將被銷毀,從而自動釋放鎖定的資源。
即使是意外的救助(在 MCVE 中throw "Haha!";),后者也會發生。
此外,我做了以下功能private:
information_t::increment()information_t::decrement()information_t::reset()
因此,不可能進行未經授權的訪問。要正確使用它們,information_t::Monitor必須獲取實體。它為public那些可以在監視器駐留的范圍內使用的函式提供包裝器,即僅由所有者執行緒使用。
轉載請註明出處,本文鏈接:https://www.uj5u.com/qukuanlian/318766.html
上一篇:如何避免沒有謂詞的虛假喚醒?
