使用防护来切换互斥锁

时间:2016-12-06 14:56:18

标签: c++ mutex

如何重构以下代码以使用推荐的lock_guards?

bool locked = false;
bool sync() {
    if (locked) {
        mutex.unlock();
    } else {
        mutex.lock();
    }
    locked = !locked;
    return locked;
}

所需的使用模式:

while (sync()) {
    // do safe things
}

基本上我试图模仿Python的with语句。例如:

from multiprocessing import Lock
with Lock():
    # do safe things

5 个答案:

答案 0 :(得分:5)

只需创建一个locker std::lock_guard<std::mutex> lock(mutex);,互联网就会在lock生命结束时自动发布。

std::mutex mutex;
....
{
     std::lock_guard<std::mutex> lock(mutex);
     // do do safe things
     // mutex will be released here
}

答案 1 :(得分:2)

只需使用范围:

{
    std::lock_guard<std::mutex> lock{mutex};

    // Your operations here
}

如果你真的想要一个带标题的范围,C ++ 17的if - with-initializer可以很容易地弯曲成这种形状:

if(std::lock_guard<std::mutex> lock{mutex}; true) {
    // Your operations here
}

...然后你可以将它隐藏在一个(有条不紊地命名的)宏中。

最后,随着我对你如何使用这个东西的所有责任的拒绝,这里有一个C ++ 14实现:

template <class T>
struct Lock {
    Lock(T &mtx)
    : guard{mtx} { }

    constexpr operator bool() const { return false; }

    std::lock_guard<T> guard;
};

// Replace the pragmas for a compiler other than Clang or GCC
// so it doesn't complain about the unused variable
#define withLock(mtx) \
    _Pragma("GCC diagnostic push") \
    _Pragma("GCC diagnostic ignored \"-Wunused-variable\"") \
    if(auto const &_lockGuard = Lock<std::remove_reference_t<decltype(mtx)>>{mtx}); else \
    _Pragma("GCC diagnostic pop")

// ...

withLock(mutex) {
    // Your operations here
}

......但实际上,一个简单的范围工作正常,并且不需要记录并反对代码审查员。

答案 2 :(得分:0)

锁定防护装置不支持您的设计,并且有充分的理由。你不应该需要切换锁。共享资源的正确使用模式是访问资源的每个线程首先通过防护获取锁,而不是使用资源,而不是存在 - 这会触发自动锁释放。

没有人需要切换锁。

答案 3 :(得分:0)

我正常接近它的一个完整例子:

#include <mutex>
#include <future>
#include <iostream>
#include <vector>
#include <chrono>
#include <random>

// here is the relevant function

template<class Mutex, class F>
decltype(auto) with_lock(Mutex& mutex, F&& func)
{
    using mutex_type = std::decay_t<Mutex>;
    using lock_type = std::lock_guard<mutex_type>;
    lock_type lock { mutex };
    return func();
}

// here is a test

int main()
{
    std::mutex m;
    std::default_random_engine eng { std::random_device()() };

    std::vector<std::future<void>> futures;
    for (int i = 0 ; i < 100 ; ++i)
    {
        futures.push_back(std::async(std::launch::async, [i, &m, &eng]
        {
            std::uniform_int_distribution<int> dist(10, 100);
            std::this_thread::sleep_for(std::chrono::milliseconds(dist(eng)));
            with_lock(m, [i]
            {
                std::cout << "thread index: " << i << std::endl;
            });
        }));
    }

    for (auto& f : futures)
    {
        f.get();
    }
}

示例输出(注意,cout上没有比赛):

thread index: 63
thread index: 62
thread index: 30
thread index: 49
thread index: 25
thread index: 1
thread index: 58
thread index: 33
thread index: 72
thread index: 75
thread index: 11
thread index: 22
thread index: 46
thread index: 41
thread index: 20
thread index: 36
thread index: 37
thread index: 23
thread index: 45
thread index: 82
thread index: 0
thread index: 28
thread index: 88
thread index: 3
thread index: 74
thread index: 84
thread index: 31
thread index: 9
thread index: 34
thread index: 93
thread index: 24
thread index: 98
thread index: 38
thread index: 55
thread index: 43
thread index: 52
thread index: 40
thread index: 69
thread index: 67
thread index: 91
thread index: 89
thread index: 86
thread index: 76
thread index: 21
thread index: 29
thread index: 53
thread index: 81
thread index: 10
thread index: 96
thread index: 68
thread index: 7
thread index: 73
thread index: 78
thread index: 54
thread index: 59
thread index: 83
thread index: 60
thread index: 47
thread index: 19
thread index: 6
thread index: 17
thread index: 56
thread index: 57
thread index: 66
thread index: 70
thread index: 39
thread index: 26
thread index: 13
thread index: 79
thread index: 15
thread index: 5
thread index: 94
thread index: 14
thread index: 77
thread index: 32
thread index: 48
thread index: 87
thread index: 92
thread index: 61
thread index: 80
thread index: 18
thread index: 27
thread index: 12
thread index: 71
thread index: 4
thread index: 2
thread index: 99
thread index: 35
thread index: 50
thread index: 51
thread index: 65
thread index: 64
thread index: 16
thread index: 42
thread index: 90
thread index: 8
thread index: 44
thread index: 85
thread index: 97
thread index: 95

答案 4 :(得分:-1)

这是一个非常邪恶,但是工作,黑客:

#include <memory>
#include <mutex>
#include <iostream>

#define with(m) for (std::unique_ptr<std::lock_guard<std::mutex>> lock( \
                     new std::lock_guard<std::mutex>(m)); lock; lock.reset())

int main()
{
  std::mutex m;
  with (m)
  {
    std::cout << "got the mutex" << std::endl;
  }
}

with(m)扩展为for循环标头

  • 为拥有互斥锁的unique_ptr创建lock_guard
  • 在循环条件中,验证指针是否为空
  • 在增量部分,重置指针。

这具有执行循环体的效果,该循环体仅跟随with()宏一次,并且保持锁定。由于宏和指针略微变暗,但是比构造while循环和切换互斥状态更清晰。

如果你有C ++ 14,你可以使用std::make_unique简化宏。如果你可以使用C ++ 17,Quentin's solution会更优雅。

当然,这并不是C ++的做法,只是为了获得一些语法糖。所以,除非你真的坚持遵循类似Python的语法,否则你可以使用标准C ++方式使用lock_guard,如下所示:

{
  std::lock_guard<std::mutex> lock(my_mutex);
  // Do whatever
}