使用锁定保护和互斥锁的首选方法是什么

时间:2018-04-05 07:32:42

标签: multithreading locking mutex c++17

类成员函数将在其mutexlock_guard上使用critical sectioncritical data。我可以看到这可以通过两种不同的方式完成。

案例1: - for循环内部。 lock_guard在每次迭代时构造和销毁。

std::mutex s_mutex;

class Foo {
public:
    void bar() {
        for ( ... ) {
            std::lock_guard<std::mutex> guard( s_mutex );
            // critical section data
        } // lock_guard goes out of scope and releases or unlocks mutex
    }
};

案例2: - for循环之外。 lock_guard创建一次,然后在循环完成后销毁。

std::mutex s_mutex;

class Foo {
public:
    void bar() {
        std::lock_guard<std::mutex> guard( s_mutex );
        for ( ... ) {
            // data
        }
    } // lock_guard goes out of scope releasing or unlocking mutex.
};

我知道在第一种情况下,一个线程可以在一次迭代中访问循环,而另一个线程可以在不同的迭代上访问循环,但没有两个线程可以同时访问临界区。至于第二种情况,我知道如果一个线程正在访问循环,第二个线程在完全完成之前就无法触及该循环。

一种方法比另一种更理想,还是取决于使用意图?是否会对另一个产生性能影响?只是想要一些澄清来试图维护现代c ++最佳实践。

1 个答案:

答案 0 :(得分:3)

您正在解锁互斥锁以立即锁定它。发生什么取决于mutex的实现方式,但典型的非公平实现会唤醒一个等待的线程,但会在该线程能够运行之前获取互斥锁,浪费执行时间。

如果您的互斥锁实现是公平的(考虑ticket lock),那么您的线程在解锁后无法锁定互斥锁,并且必须等到另一个线程离开临界区。这意味着在竞争中你的线程必须在每次迭代时进行上下文切换,浪费执行时间。

所以第二种情况(循环外部的互斥体)在公平和非公平的互斥体实现方面应该更有效,这就是你应该做的。

现在你可以考虑在每次迭代时锁定互斥锁,如果你关心延迟,因为这允许其他线程运行,但这只有合理的互斥实现才有意义。

C ++没有说明std::mutex是否合理,大多数实现都不公平。所以不要期待太多。

所以唯一合理且便携的方法是将锁放在循环之外。因为即使您关心延迟,std::mutex也不能帮助您。