Удалите определения в этом примере защитного мьютекса - PullRequest
1 голос
/ 14 июня 2011

- я не могу экспериментировать с банкоматом, но буду сегодня вечером.Я думаю, что typedef может использоваться для хранения mut и может использоваться для объявления переменной var.Но я изначально думал, что typedefs не очень хорошо работают с шаблонами, поэтому я должен проверить позже вечером (сейчас, в классе)

Я смотрел на этот фрагмент кода, показанный ниже и мне было интересно, как это можно реализовать без использования определений.

Поскольку я не могу скомпилировать код (у меня в настоящее время нет никаких библиотек мьютекса / многопоточности), я просто посмотрю на коди подумайте.

Кажется, что можно полностью реализовать PROTECTED_WITH, унаследовав класс шаблона.Проблема сейчас PROTECTED_MEMBER.Он использует имя с ## для создания переменной.Это не является большой проблемой, потому что мы создаем класс, который содержит переменную с оператором (), чтобы она отображалась как функция.Однако при доступе к is_held() проблема, поскольку я хотел бы не пропускать this или mut_ in.

Моя интуиция говорит «из коробки», думая, что можно решить эту проблему без определений и без перехода ккаждая переменная a this, функция ptr или ссылка.Я позволю всем обманывать и использовать функции c ++ 0x.

template<typename Mutex>
class TestableMutex {
public:
     void lock()      {  m.lock();  id = this_thread::get_id();  }
     void unlock()    {  id = 0;  m.unlock();  }
     bool try_lock()  {  bool b = m.try_lock();
                 if( b ) id = this_thread::get_id();
                 return b;  }
     bool is_held()    { return id == this_thread::get_id(); }
private:
     Mutex m;
     atomic<thread::id> id;
     // for recursive mutexes, add a count
};

#define PROTECTED_WITH(MutType)  \
     public:    void    lock()        {  mut_.lock();  } \
     public:    bool    try_lock()    {  return mut_.try_lock();  } \
     public:    void    unlock()      {  mut_.unlock();  } \
     private:    TestableMutex<MutType> mut_;
#define PROTECTED_MEMBER(Type,name) \
     public:    Type&   name()        { assert(mut_.is_held()); return name##_; } \
     private:    Type   name##_;

struct MyData {
     PROTECTED_WITH( some_mutex_type );
     PROTECTED_MEMBER( vector<int>, v );
     PROTECTED_MEMBER( Widget*, w );
};

Ответы [ 2 ]

1 голос
/ 16 июня 2011

Вы можете использовать явную специализацию, содержащую объявления using, чтобы перечислить объекты, защищенные мьютексом. Затем используйте базовый класс, чтобы «передать» доступ пользователю через operator->, поэтому object->memberobject , а не указателем) выполняет утверждение мьютекса.

Это легче сделать, чем сказать:

// Imagine that the members of this class must be locked by the mutex.
class a : public expose_locked_by_arrow< a > {
protected:
    int i;
    void f();
};

// Declare which members are conditionally locked. Pretty simple and idiomatic.
template<>
struct member_expose< a > : a {
    using a::i;
    using a::f;
};

#include <iostream>

// Access mutex-locked members with ->
int main() {
        a x;
        x->i = 5;
        a const y( x );
        std::cout << y->i << '\n';
}

Код библиотеки:

// This template is specialized for each mutex protection client.
template< class >
struct member_expose;

// Base class provides mutex; parameter is derived class (CRTP).
template< class c >
struct expose_locked_by_arrow {
    member_expose< c > *
    operator->() {
        assert ( expose_lock_mutex.is_held() );
        return static_cast< member_expose< c > * >( this );
    }

    member_expose< c > const *
    operator->() const {
        assert ( expose_lock_mutex.is_held() );
        return static_cast< member_expose< c > const * >( this );
    }

    expose_locked_by_arrow( mutex const &m = mutex() )
        : expose_lock_mutex( m ) {}

protected:
    mutex expose_lock_mutex;
};

См. Это запустить .

1 голос
/ 15 июня 2011

#define не предоставляют никакой защиты как таковой, скорее, они просто уменьшают количество набираемых вами символов (в свою очередь, они гарантируют, что все «защищенные» члены имеют правильный код вplace).

Я не знаю, как избежать необходимости помещать проверки в каждую функцию получения - и блокировать весь объект, так как они возвращают ссылки на данные, хранящиеся в защищенном объекте.,

Если, однако, все они могут быть возвращены по значению (или вообще ничего не возвращать), то вы можете использовать контейнер, который блокирует все, используя прокси-объект, что-то вроде следующего (это, вероятно, можно сделать лучше, я просто быстро взломал его вместе):

#include <iostream>

struct Mutex
{
    void lock()
    {
        std::cout << "Mutex::lock" << std::endl;
    }

    void unlock()
    {
        std::cout << "Mutex::unlock" << std::endl;
    }
};

template <class Object>
class ThreadSafeObject
{
    mutable Mutex  d_mutex;
    Object         d_object;

  public:
    struct Proxy
    {
        mutable Mutex *d_mutex;
        Object        *d_object;

        Proxy(Mutex *mutex, Object *object)
        : d_mutex(mutex)
        , d_object(object)
        {
            d_mutex->lock();
        }

        Proxy(const Proxy& proxy)
        : d_mutex(proxy.d_mutex)
        , d_object(proxy.d_object)
        {
            proxy.d_mutex = NULL;
        }

        ~Proxy()
        {
            if (d_mutex)
            {
                d_mutex->unlock();
            }
        }

        Object *operator->()
        {
            return d_object;
        }
    };

    struct ConstProxy
    {
        mutable Mutex *d_mutex;
        const Object  *d_object;

        ConstProxy(Mutex *mutex, const Object *object)
        : d_mutex(mutex)
        , d_object(object)
        {
            d_mutex->lock();
        }

        ConstProxy(const ConstProxy& proxy)
        : d_mutex(proxy.d_mutex)
        , d_object(proxy.d_object)
        {
            proxy.d_mutex = NULL;
        }

        ~ConstProxy()
        {
            if (d_mutex)
            {
                d_mutex->unlock();
            }
        }

        const Object *operator->() const
        {
            return d_object;
        }
    };

    Proxy operator->()
    {
        return Proxy(&d_mutex, &d_object);
    }

    ConstProxy operator->() const
    {
        return ConstProxy(&d_mutex, &d_object);
    }
};

struct Foo
{
    void foo()
    {
        std::cout << "Foo::foo" << std::endl;
    }
};

int main()
{
    ThreadSafeObject<Foo> myFoo;
    myFoo->foo();
    return 0;
}

Использует трюк оператора -> () (, когда operator-> не возвращает тип указателя, компилятор будет продолжать вызывать operator-> в возвращаемых значениях до тех пор, пока в конечном итоге не будет возвращен обычный тип указателя ), который выдаст следующий результат:

Mutex::lock
Foo::foo
Mutex::unlock

В общем случае, объект, который должен использоваться несколькими потоками, не долженвыставляя свои внутренние компоненты таким образом, было бы безопаснее, чтобы он принимал параметры и использовал свои внутренние значения для воздействия на них.

...