【问题标题】:Is there a sort fo weak mutex concept?有一种弱互斥锁的概念吗?
【发布时间】:2017-10-06 09:42:04
【问题描述】:

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

【问题讨论】:

  • 我认为你想要的是shared_mutex - 你想要messupThreadlock() / unlock() 通常和workerThreadlock_shared() / unlock_shared()
  • 相信你可能在找std::shared_mutex
  • 这种模式被称为“Single Writer/Multiple Reader”。
  • 这个答案可能有用:stackoverflow.com/questions/39185420/…

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


【解决方案1】:

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

让工作线程使用shared lock,让混乱线程使用unique lock

首选共享访问还是唯一访问,depends on the implementation。因此,您可能会看到由于大量工作线程非常频繁地占用共享锁而导致混乱线程饿死。

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

【讨论】:

  • 那你直接使用pthread库和里面的读写互斥量就可以了。或者您可以使用 Boost,它还提供锁定功能。
  • @jpo38 有趣的是std::shared_lock 是 C++14 标准的一部分。因此,您可以将其与不同的 shared_mutex 实现结合使用,并在您继续使用 C++17 时替换它。
  • @jpo38 在C++14 你有std::shared_timed_mutex 可以工作。
  • @Galik:实际上,我正在使用 boost,它提供了 shared_mutex。只需在提议的代码中将std 替换为boost
【解决方案2】:

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

【讨论】:

  • 其实我在 MVCE 中使用了shared_ptr(为了避免发布内存泄漏的代码),但是我的原始代码使用的是原始指针...
  • @jpo38 也许你应该考虑真正使用共享指针? :)
猜你喜欢
  • 2012-03-08
  • 1970-01-01
  • 2018-09-24
  • 2011-07-14
  • 2012-06-05
  • 2019-08-11
  • 2018-05-23
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多