在C ++中模拟C#lock语句

时间:2017-12-01 22:42:20

标签: c++ multithreading locking

简介:对于同步,C#提供System.Threading.Monitor类,提供线程同步例程,例如Enter()Exit()TryEnter()

此外,还有lock语句确保在关键代码块被丢弃时,通过正常执行流或异常来锁定锁:

private static readonly obj = new Object();

lock(obj) {
   ...
}

问题:在C ++中,为此目的,我们得到了RAII包装器std::lock_guardstd::unique_lock,它们不适用于Monitor类,而是适用于类型Lockable概念。但是,我认为这种方法在语法上比C#实现它的方式更弱,原因如下:

您使用无法重复使用的变量名污染本地范围。这可以通过添加新的范围来抵消,例如

  {
      std::unique_lock<std::mutex> lck{ mtx };
      ...
  }

但我发现这种符号看起来很尴尬。更让我感到困扰的是这是有效的C ++:

std::unique_lock<std::mutex>{ mtx ]; // note there is no name to the lock!
...

因此,如果忘记给锁定卫士一个正确的名字,这个陈述将被解释为一个名为&#34; mtx&#34;的变量声明。类型std::unique_lock<std::mutex>,没有任何锁定!

我想在C ++中实现类似C#的lock语句。在C ++ 17中,这可以很容易地完成:

#define LOCK(mutex) if(std::lock_guard<decltype(mutex)> My_Lock_{ mutex }; true)

std::mutex mtx;
LOCK(mtx) {
   ...
}

问:我如何在C ++ 11/14中实现它?

3 个答案:

答案 0 :(得分:2)

暂且不说&#34;如果你这样做&#34;,请按照以下方式:

虽然它并不完全相同,因为它需要一个分号,它足够接近我觉得我可能会呈现它。这个纯C ++ 14解决方案基本上只是定义宏来启动一个立即执行的lambda:

template<typename MTX>
struct my_lock_holder {
    MTX& mtx;
    my_lock_holder(MTX& m) : mtx{m} {}
};

template<typename MTX, typename F>
void operator+(my_lock_holder<MTX>&& h, F&& f) {
    std::lock_guard<MTX> guard{h.mtx};
    std::forward<F>(f)();
}

#define LOCK(mtx) my_lock_holder<decltype(mtx)>{mtx} + [&]

my_lock_holder稍后会获取互斥锁参考,并允许我们重载operator+。这个想法是操作员创建警卫并执行lambda。正如您所看到的,宏定义了一个默认的引用捕获,因此lambda将能够引用封闭范围内的任何内容。然后它非常直接:

std::mutex mtx;
LOCK(mtx) {

}; // Note the semi-colon

你可以看到它构建 live

答案 1 :(得分:1)

受到StoryTeller的好主意的启发,我认为自己找到了一个可行的解决方案,尽管它有点像&#34; hack&#34;:

template <typename T>
struct Weird_lock final : private std::lock_guard<T> {
    bool flip;
    Weird_lock(T& m) : std::lock_guard<T>{ m }, flip{ true } { }

    operator bool() noexcept {
        bool old = flip;
        flip = false;
        return old;
    }
};

#define LOCK(mutex) for(Weird_lock<decltype(mutex)> W__l__{ mutex }; W__l__;)

好消息是它最终不需要分号。不好的是需要额外的bool,但是从我在godbolt.org中看到的情况来看,编译器无论如何都会优化它。

答案 2 :(得分:0)

我建议你这样做:

#define UNIQUE_NAME(name) name##__COUNTER__
#define LOCK(mutex) std::lock_guard<decltype(mutex)> UNIQUE_NAME(My_Lock){ mutex };

使用 COUNTER 预处理程序符号将生成一个您根本不关心的唯一变量名称。