问题描述
我想确保一次只有一个线程可以运行我的 C++ 类的方法.换句话说,让类表现得像一个 Monitor.
I want to ensure that only one thread at a time can run a method of my C++ class. In other words, make the class behave like a Monitor.
有没有一种模式、模板化的方法可以做到这一点,或者我可以使用一些 Boost 类?因为到目前为止我唯一的想法是添加一个临界区成员,并在每个方法的开头获取它并在最后释放它(当然使用 RAII).但这似乎很多余,我无法将其重用于其他类.
Is there a pattern, templatized way to do this, or some Boost class I can use? Because my only idea so far is adding a Critical Section member, and acquire it at the beginning of each method and release it at the end (using RAII, of course). But that seems very redundant, and I can't reuse it for some other class.
推荐答案
您可以通过明智地使用 operator->
和现代 C++ 来实现这一点,这提供了比以前接受的更简洁的语法答案:
You can achieve this with some judicious use of operator->
and modern c++ which gives for much cleaner syntax than the previously accepted answer:
template<class T>
class monitor
{
public:
template<typename ...Args>
monitor(Args&&... args) : m_cl(std::forward<Args>(args)...){}
struct monitor_helper
{
monitor_helper(monitor* mon) : m_mon(mon), m_ul(mon->m_lock) {}
T* operator->() { return &m_mon->m_cl;}
monitor* m_mon;
std::unique_lock<std::mutex> m_ul;
};
monitor_helper operator->() { return monitor_helper(this); }
monitor_helper ManuallyLock() { return monitor_helper(this); }
T& GetThreadUnsafeAccess() { return m_cl; }
private:
T m_cl;
std::mutex m_lock;
};
这个想法是你使用箭头运算符来访问底层对象,但它返回一个帮助对象,它锁定然后解锁你的函数调用周围的互斥锁.然后通过重复应用 operator->
的语言的魔力,您可以获得对底层对象的引用.
The idea is that you use the arrow operator to access the underlying object, but that returns a helper object which locks and then unlocks the mutex around your function call. Then through the magic of the language repeatedly applying operator->
you get a reference to the underlying object.
用法:
monitor<std::vector<int>> threadSafeVector {5};
threadSafeVector->push_back(0);
threadSafeVector->push_back(1);
threadSafeVector->push_back(2);
// Create a bunch of threads that hammer the vector
std::vector<std::thread> threads;
for(int i=0; i<16; ++i)
{
threads.push_back(std::thread([&]()
{
for(int i=0; i<1024; ++i)
{
threadSafeVector->push_back(i);
}
}));
}
// You can explicitely take a lock then call multiple functions
// without the overhead of a relock each time. The 'lock handle'
// destructor will unlock the lock correctly. This is necessary
// if you want a chain of logically connected operations
{
auto lockedHandle = threadSafeVector.ManuallyLock();
if(!lockedHandle->empty())
{
lockedHandle->pop_back();
lockedHandle->push_back(-3);
}
}
for(auto& t : threads)
{
t.join();
}
// And finally access the underlying object in a raw fashion without a lock
// Use with Caution!
std::vector<int>& rawVector = threadSafeVector.GetThreadUnsafeAccess();
rawVector.push_back(555);
// Should be 16393 (5+3+16*1024+1)
std::cout << threadSafeVector->size() << std::endl;
这篇关于使 C++ 类成为监视器(在并发意义上)的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持!