是否有一种弱互斥概念?

时间:2017-10-06 09:42:04

标签: c++ multithreading c++11 c++14 mutex

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

workerThreadFoo实例执行操作。 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 );
    }
}

2 个答案:

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