【问题标题】:C++ monitor class/wrapper using condition variables使用条件变量的 C++ 监视类/包装器
【发布时间】:2019-09-09 15:55:13
【问题描述】:

我正在尝试在 C++ 中创建一个包装类 W,它是使用指向通用对象 OBJ 的指针构造的。

当您通过W 调用OBJ 方法之一时,W(包含条件变量cv)在调用OBJ 方法之前发出cv.wait(),当OBJ 方法时发出cv.notify()已经结束了。

我已经能够通过继承特定类来做到这一点,但想要一种像上面描述的通用方法。

这是继承方式:

struct A
{
    virtual void foo(int i) { bar = i; };
    int bar;
};

struct B : public A
{
    void foo2(int i)
    {
        cv.wait(lck);
        this->foo(i);
        cv.notify_one();
    }
    std::condition_variable cv;
    std::unique_lock<std::mutex> lck;
};

我想要类似的东西:

template<class T>
struct C
{
    C(T *t) : t(t) {}

    T *operator->()
    {
        cv.wait(lck);
        return t;
        // notify_one when function has completed
    }

    T *t;
    std::condition_variable cv;
    std::unique_lock<std::mutex> lck;
};

我找到了只使用锁的答案(但这并不是真正的监视器):https://stackoverflow.com/a/48408987

【问题讨论】:

  • 你可能想要一个Finally 类,就像这个one
  • 几年前,安德烈亚历山德雷斯库在演讲中展示了这一点......
  • 您的代码有问题。 UB 我相信。您应该存储一个互斥锁,并且每次都从它创建一个锁。如果你创建一个锁并从不同的线程访问它会发生什么? IDK,但没什么好。
  • 我认为是这样的:youtube.com/watch?v=ozOgzlxIsdg

标签: c++ concurrency monitor


【解决方案1】:

这是一个工作示例:

#include <iostream>
#include <mutex>

template<class T>
class Wrapper {
    std::mutex m;
    T* p;

public:
    Wrapper(T& t) : p(&t) {}

    class Proxy {
        std::unique_lock<std::mutex> lock;
        T* p;

    public:
        Proxy(std::mutex& m, T* p)
            : lock(m)
            , p(p)
        {
            // Locked the mutex.
            std::cout << __PRETTY_FUNCTION__ << '\n';
        }

        Proxy(Proxy&& b) = default;

        ~Proxy() {
            std::cout << __PRETTY_FUNCTION__ << '\n';
            // Unlocked the mutex.
        }

        T* operator->() const { return p; }
    };

    Proxy operator->() { return Proxy(m, p); }
};

struct A {
    void f() { std::cout << __PRETTY_FUNCTION__ << '\n'; }
};

int main() {
    A a;
    Wrapper<A> w(a);
    w->f();
}

输出:

Wrapper<T>::Proxy::Proxy(std::mutex&, T*) [with T = A]
void A::f()
Wrapper<T>::Proxy::~Proxy() [with T = A]

详情请参阅Wrapping C++ Member Function Calls by Bjarne Stroustrup

【讨论】:

  • 我比我更喜欢这个。 +1
  • 当已经有另一个线程访问该对象的方法时,我需要使用条件变量使试图访问该对象方法的线程进入睡眠状态。这与原帖中的链接几乎相同。
  • @sornbro 他们有。 Learn how mutex works。不要将互斥锁与自旋锁混淆。
  • @sornbro condition_variable 在您的代码中完全不合适。另外,一般我不推荐这种互斥锁,它只适用于非常特定的场景。如果您假设操作之间没有外部干扰,您可能很容易得到一个令人讨厌的错误。
  • @sornbro 如果互斥锁已被锁定,则锁定互斥锁阻塞,直到互斥锁可用。 阻塞 意味着线程从 CPU 中取消调度。在此处查看有关互斥锁的更多信息:man7.org/linux/man-pages/man3/pthread_mutex_lock.3p.html
【解决方案2】:

这不是很好,但是您可以做的是让用户从包装器中调用一个函数,然后提供一个 lambda 来调用他们实际想要使用的函数。这使得使用包装器变得更加冗长,但它允许您包装任何类型,而无需尝试弄清楚如何转发其所有功能(通常确实需要重新选择)。这会给你一个像

这样的包装器
template<class T>
struct C
{
    C(T *t) : t(t) {}

    template<typename Func>
    void call(Func func)
    {
        wait operation
        func(t);
        notify operation
    }

    T *t;
};

然后你会像这样使用它

Foo_ptr foo =  ...;
C wrapper(foo);
wrapper.call([](auto ptr){ return ptr->func_I_want_to_call(arguments); });

这将使用提供的参数从包装器中调用t 上的func_I_want_to_call

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2015-09-28
    • 1970-01-01
    • 2011-06-17
    • 1970-01-01
    • 2020-07-31
    • 1970-01-01
    • 2022-01-25
    相关资源
    最近更新 更多