有一种弱互斥概念吗?

Is there a sort fo weak mutex concept?

让我们考虑一下这段代码(不一定有意义,它只是一个 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 );
    }
}

A workerThreadFoo 实例进行操作。 A messupThread 修改应在其上执行操作的实例。

这段代码是线程安全的。

如果启动 10 workerThread 就会出现问题,当一个 workerThread 使用该实例时,它将锁定其他九个(而 doSomething 是线程安全的,它们应该能够同时工作)。

是否有一种弱 mutex/lock 机制可以使任何 workerThread 阻止 messupThread 更改实例但不会阻止并发 workerThread 使用它.

我可以这样做,但我想知道是否有更简单的方法来使用标准 objects/mechanisms:

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 );
    }
}

如果所有线程都应该共享同一个智能指针 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);
    }
}

如果您可以访问 std::shared_mutex,它可能会很好地解决您的问题。它基本上是一个 read/write 锁,但具有不同的命名法:共享访问与唯一访问。

让工作线程占用shared lock, and let the messup thread take a unique lock

共享访问还是独有访问是首选,。所以你可能会看到你的混乱线程因为大量的工作线程非常频繁地获取共享锁而饿死。

// 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 标准的一部分。