C++;使用条件变量监视类/包装器 我试图在C++中创建一个包装类 w ,它是用一个指针指向一个通用对象 Obj> /Cuff>

C++;使用条件变量监视类/包装器 我试图在C++中创建一个包装类 w ,它是用一个指针指向一个通用对象 Obj> /Cuff>,c++,concurrency,monitor,C++,Concurrency,Monitor,当您通过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) {

当您通过
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;
};
结构A { 虚空foo(inti){bar=i;}; int-bar; }; 结构B:公共A { 2(内部一) { cv.等待(lck); 本->富(i); cv.通知_one(); } std::条件变量cv; std::唯一锁定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;
};
模板
结构C
{
C(T*T):T(T){}
T*运算符->()
{
cv.等待(lck);
返回t;
//功能完成时通知_one
}
T*T;
std::条件变量cv;
std::唯一锁定lck;
};

我发现了一个只使用锁的答案(但这不是真正的监视器):

这不太好,但是你可以做的是让用户从包装器调用一个函数,然后你提供一个lambda来调用他们真正想要使用的函数。这使得使用包装器更为冗长,但它允许您包装任何类型,而无需尝试找出如何转发其所有函数(这确实需要refelection一般地完成)。那会给你一个像这样的包装

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;
};

这将使用提供的参数从包装器调用
func\u I\u want\u调用
t

下面是一个工作示例:

#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();
}
#包括
#包括
模板
类包装器{
std::互斥m;
T*p;
公众:
包装器(T&T):p&T{}
类代理{
std::唯一的_锁;
T*p;
公众:
代理(std::mutex&m,T*p)
:锁(米)
,p(p)
{
//锁定互斥锁。

std::你可能不希望最后有一个这样的
类。几年前,Andrei Alexandrescu在演讲中展示了这一点…你的代码被窃听了。我相信。你应该存储一个互斥锁,每次都从它创建一个锁。如果你创建一个锁并从不同的线程访问它,会发生什么?IDK,但没有什么好的。我想k这是这样的:我比我更喜欢这个。+1当已经有另一个线程访问该对象的方法时,我需要使用条件变量使试图访问该对象的方法的线程进入睡眠状态。这几乎与原始帖子中的链接相同。@sornbro他们喜欢。。不要将互斥锁与自旋锁混淆。@sornbro
condition_变量
在您的代码中完全不合适。另外,通常我不推荐这种类型的互斥锁,它只适用于非常特定的场景。如果您假设操作之间没有外部干扰,则很容易出现严重的错误。@sornbro如果互斥锁已经锁定,则锁定互斥锁块直到互斥体变为可用。阻塞意味着线程在CPU之外被取消调度。请参阅此处有关互斥体的更多信息:
#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]