[英]Is std::mutex as a member variable thread-safe for multiple threads?
[英]Thread-safe locking of instance with multiple member functions
我有一個被多個線程使用的結構實例。 每個線程包含未知數量的 function 調用,這些調用會更改結構成員變量。
我有一個專用的 function 試圖為當前線程“保留”結構實例,我想確保在原始線程允許之前沒有其他線程可以保留該實例。
互斥鎖浮現在腦海中,因為它們可用於保護資源,但我只知道單個 function 的 scope 中的 std::lock_guard,但不為鎖定和解鎖之間的所有 function 調用添加保護。
當我知道它總是按順序調用保留和釋放時,是否可以保護這樣的資源?
更好地解釋它的片段:
#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;
}
我對每條評論的建議:
一個更好的習慣用法可能是一個監視器,它可以鎖定您的資源並為所有者提供訪問權限。 要獲取資源,
reserve()
可以返回此類監視器 object(類似於訪問資源內容的代理)。 任何對reserve()
的競爭訪問現在都會被阻止(因為互斥體被鎖定)。 當擁有資源的線程完成后,它只會銷毀監視器 object,從而解鎖資源。 (這允許將 RAII 應用於所有這些,從而使您的代碼安全且可維護。)
我修改了 OPs 代碼以勾勒出它的樣子:
#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;
}
Output:
Result: 2
Result: -1
Result: 0
當我知道它總是按順序調用保留和釋放時,是否可以保護這樣的資源?
沒有必要再擔心這個了。 正確的用法是燒在:
后者甚至會發生意外救助(在 MCVE 中throw "Haha;";
)。
此外,我將以下功能private
:
information_t::increment()
information_t::decrement()
information_t::reset()
因此,沒有未經授權的訪問是不可能的。 要正確使用它們,必須獲取一個information_t::Monitor
實例。 它為那些可以在監視器所在的 scope 中使用的函數提供public
包裝器,即僅由所有者線程使用。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.