【问题标题】:many locks on same mutex, one locks forever同一个互斥锁上的许多锁,一个永远锁
【发布时间】:2017-02-14 03:24:27
【问题描述】:

在我的程序中,我有一个互斥体和两个线程。这些线程之一经常获取锁。另一个线程尝试获取但必须永远等待。

会不会是锁在释放之后很快就被获取了,以至于其他线程没有机会?互斥锁总是给每个人机会吗?如果没有,什么是好的解决方案?(某种 FIFO 锁?)

我正在使用 std::mutex 和 std::lock_guard

问题扩展 seccpur 指出 std::condition_variable 可以解决这个问题。三个线程如何扩展? std::condition_variable 是否确保每个线程都转一圈?假设你使用 notify_one()。

【问题讨论】:

  • 条件变量在你的情况下应该可以正常工作
  • 你在使用 std::mutex 和 std::lock_guard 吗? std::condition_variable?很高兴看到一些代码。
  • 我正在使用 std::mutex 和 std::lock_guard。
  • @seccpur condition_variable 如何使用三个线程进行扩展?会不会是两个线程一起饿死了第三个线程?
  • @AartStuurman:使用 notify_all 广播到多个线程

标签: c++ locking


【解决方案1】:

std::mutex 不保证给每个人平等的机会。因此,一个线程可能会饿死另一个线程。您可以尝试的第一件事是插入std::this_thread::yield() 看看它是否有帮助。如果这没有帮助,那么您的代码一定有逻辑错误。发布部分代码,我们可以帮助您进一步诊断。

【讨论】:

  • 感谢您的建议。让步确实解决了问题,这就是为什么我认为一个线程饿死另一个线程必须是问题所在。
  • 这是什么平台?
【解决方案2】:

使用 seccpur 的提示,我想出了以下解决方案来防止单个线程耗尽。

#include <condition_variable>
#include <mutex>
#include <atomic>

class NoStarveLock
{
    std::condition_variable condition;
    std::atomic_flag flag = ATOMIC_FLAG_INIT;
    std::mutex conditionLock;
public:
    void lock()
    {
        std::unique_lock<std::mutex> lck(conditionLock);
        while (flag.test_and_set()) // multiple threads can wake up at the same time, so use a set+test
        {
            condition.wait(lck); // wait for a wakeup
        }
    }

    void unlock()
    {
        std::unique_lock<std::mutex> lck(conditionLock);
        flag.clear();
        condition.notify_all();
    }
};

【讨论】:

  • 不错的解决方案,但它似乎可能存在一些可伸缩性问题,因为如果少数线程同时从等待中释放,则您不能保证哪个线程会首先设置标志并且线程可以被其他线程耽误了我建议你看看这个答案中提供的解决方案(修复奥拉夫的建议)stackoverflow.com/a/14792685/4834897
  • 这仍然可能是不公平的。假设线程 A 拥有标志,而 B 和 C 正在等待。 A 的 notify_all 唤醒两个线程,B 赢得比赛并获得标志。当 B 完成时,它唤醒了 A 和 C,A 赢得了比赛。 A 和 B 继续赢得比赛,而 C 被饿死了。如果您想要 FIFO,您需要跟踪谁等待的时间最长,并确保他们下一个获得标志。即使只有两个线程 A 和 B,这也可能是不公平的:A 可以释放标志并 notify_all 唤醒 B,但在 B 可以声明标志之前,A 立即执行 lock() 并再次声明。
【解决方案3】:

使用std::mutexstd::lock_guardstd::unique_lock(读者)和std::condition_variable的示例。 _mutexLockCondition、_mutexObject 和 _dataContainer 在 ReaderClass 和 WriterClass 之间共享。

试图对实际的数据容器有点模糊,所以getDataContainer()addDataToContainer() 由你决定。

std::shared_ptr< Data > ReaderClass::read()
{
    std::unique_lock< std::mutex > lock( _mutexObject );

    // handle spurious wakeup from waitForMessageNotification
    while( _dataContainer.empty() )
    {
        if( waitForMessageNotification( lock ) )
        {
            // timeout occurred, return nullptr to prevent blocking
            return nullptr;
        }
    }

    return getDataFromContainer();
}

bool ReaderClass::waitForNotification( unique_lock< mutex > & lock )
{
    //_mutexLockCondition is a std::condition_variable
    return _mutexLockCondition.wait_for( lock, std::chrono::milliseconds( 100 ) )
                                            == std::cv_status::timeout;
}


void WriterClass::write( std::shared_ptr< Data > dataPtr )
{
    std::lock_guard< mutex > lock( _mutexObject );

    addDataToContainer( dataPtr );

    _mutexLockCondition.notify_one();
}

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2023-03-15
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2011-03-03
    相关资源
    最近更新 更多