让我们考虑一下这段代码(不一定有意义,它只是一个MCVE):
class Foo
{
public:
// this function is thread safe
void doSomething();
};
static std::mutex mutex;
static std::shared_ptr<Foo> instance;
void workerThread()
{
while ( true )
{
mutex.lock();
if ( instance )
{
instance->doSomething();
}
mutex.unlock();
msleep( 50 );
}
}
void messupThread()
{
while ( true )
{
mutex.lock();
instance.reset( new Foo() );
mutex.unlock();
msleep( 1000 );
}
}
workerThread
对Foo
实例执行操作。 messupThread
修改应在其上执行操作的实例。
此代码是线程安全的。
如果你开始10 workerThread
就会出现问题,当一个workerThread
使用该实例时,它将锁定其他九个(虽然doSomething
是线程安全的,但它们应该可以工作同时地)。
是否存在一种弱的互斥/锁定机制,可以使任何workerThread
阻止messupThread
更改实例,但不会阻止并发workerThread
使用它。
我可以做到这一点,但我想知道是否有更简单的方法来实现使用标准对象/机制:
class Foo
{
public:
// this function is thread safe
void doSomething();
};
static int useRefCount = 0;
static std::mutex work_mutex; // to protect useRefCount concurrent access
static std::mutex mutex; // to protect instance concurrent access
static std::shared_ptr<Foo> instance;
void workerThread()
{
while ( true )
{
work_mutex.lock();
if ( useRefCount == 0 )
mutex.lock(); // first one to start working, prevent messupThread() to change instance
// else, another workerThread already locked the mutex
useRefCount++;
work_mutex.unlock();
if ( instance )
{
instance->doSomething();
}
work_mutex.lock();
useRefCount--;
if ( useRefCount == 0 )
mutex.unlock(); // no more workerThread working
//else, keep mutex locked as another workerThread is still working
work_mutex.unlock();
msleep( 50 );
}
}
void messupThread()
{
while ( true )
{
mutex.lock();
instance.reset( new Foo() );
mutex.unlock();
msleep( 1000 );
}
}
答案 0 :(得分:4)
如果您可以访问std::shared_mutex
,则可以很好地解决您的问题。它基本上是一个读/写锁,但具有不同的命名:共享访问与唯一访问。
让工作线程取一个shared lock,然后让麻烦线程取unique lock。
首选{@ 3}}是首选访问权限还是唯一权限。所以你可能会看到你的垃圾邮件线程因为大量的工作线程经常占用共享锁而变得饥饿。
// Worker thread:
std::shared_lock<std::shared_mutex> lock(m_mutex);
// Messup thread:
std::unique_lock<std::shared_mutex> lock(m_mutex);
请注意std::shared_mutex
是C ++ 17标准的一部分。
答案 1 :(得分:1)
如果所有线程都应该共享同一个智能指针instance
,那么使用std::shared_ptr
开头是没有意义的,而是std::unique_ptr
。
我是否可以建议每个线程都有拥有 std::shared_ptr
,从全局instance
共享指针复制?然后,由锁保护,从全局共享指针复制到本地共享指针,当解锁时,通过本地共享指针调用doSomething
函数。
也许像
void workerThread()
{
std::shared_ptr<Foo> local_instance;
while (true)
{
{
std::scoped_lock lock(mutex);
local_instance = instance;
}
if (local_instance)
{
local_instance->doSomething();
}
msleep(50);
}
}