Policy benutzen, um Threading-Verhalten in Klasse zu geben



  • Hab da mal was gebastelt.

    Folgendes ist aber Quark, weil scoped_lock natürlich nicht funktioniert. Den RAII-Komfort von scoped_lock will ich aber nicht verlieren. Wie bringe ich das in der Policy unter?

    #include <iostream>
    #include <boost/thread.hpp>
    using namespace std;
    
    class BoostThreader {
    public:
        typedef boost::mutex Mutex;
        void lock(Mutex& the_lock) {
            boost::mutex::scoped_lock scoped_lock(the_lock);
        }
    };
    
    class SingleThreaded {
    public:
        typedef void* Mutex;
        void lock(Mutex&) {}
    };
    
    template <class ThreadingPolicy>
    class Foo: public ThreadingPolicy {
    public:
        typedef typename ThreadingPolicy::Mutex Mutex;
        using ThreadingPolicy::lock;
        void bar() {
            lock(lock_);
            cout << "Bar!" << endl;
        }
    private:
        Mutex lock_;
    };
    
    int main(int argc, char *argv[])
    {
        Foo<BoostThreader> my_threaded_foo;
        my_threaded_foo.bar();
    
        Foo<SingleThreaded> my_singlethreaded_foo;
        my_singlethreaded_foo.bar();
    
        return 0;
    }
    

  • Administrator

    Wieso erbst du von der Policy? Und wieso überhaupt so kompliziert? 🙂
    Grundsätzlich musst du nur eine neue Mutex-Klasse erstellen, welche das Lockable Konzept von Boost unterstützt, aber tatsächlich nichts tut:

    struct no_mutex
    {
      void lock() { }
      void unlock() { }
      bool try_lock() { return true; }
    };
    

    Und nun zu deiner Foo-Klasse:

    template<typename ThreadingMutexT>
    class Foo
    {
    private:
      ThreadingMutexT m_mutex;
    
    public:
      void bar()
      {
        boost::lock_guard<ThreadingMutexT> guard(m_mutex);
      }
    };
    

    Und das ganze in der Anwendung:

    int main()
    {
      Foo<boost::mutex> mutex_foo;
      Foo<no_mutex> no_mutex_foo;
    
      mutex_foo.bar();
      no_mutex_foo.bar();
    
      return 0;
    }
    

    Grüssli



  • Das Problem ist, dass ich boost komplett wegkapseln muss, da der single-threaded teil des projekts kein boost zur verfügung hat.

    Daher darf in der Klasse kein boost-Code drinstehen, der muß über die Policy reinkommen. Das ging vorher halt über ein #ifdef.


  • Administrator

    Dann schreib halt eben deinen eigenen Lock-Guard. Ist ja auch nicht weiters wild:

    template<typename MutexT>
    class scoped_lock
    {
    private:
      MutexT& m_mutex;
    
    public:
      scoped_lock(Mutex& mutex)
       : m_mutex(mutex)
      {
        m_mutex.lock();
      }
    
      ~scoped_lock()
      {
        m_mutex.unlock();
      }
    
    private:
      scoped_lock(scoped_lock const&) { }
      scoped_lock& operator =(scoped_lock const&) { }
    };
    

    Statt boost::lock_guard verwendest du halt nun scoped_lock .

    Grüssli



  • Das scheint genau das zu leisten, was ich brauche:

    #include <iostream>
    #include <boost/thread.hpp>
    #include <QMutexLocker>
    using namespace std;
    
    class BoostThreaded {
    public:
        typedef boost::mutex Mutex;
        typedef boost::mutex::scoped_lock ScopedLock;
    protected:
        Mutex lock_;
    };
    
    class QThreaded {
    public:
        typedef QMutex Mutex;
        typedef QMutexLocker ScopedLock;
        QThreaded():lock_(new QMutex) {};
        ~QThreaded() { delete lock_; }
    protected:
        Mutex* lock_;
    };
    
    class SingleThreaded {
    public:
        typedef void* Mutex;
        typedef void* ScopedLock;
    protected:
        Mutex lock_;
    };
    
    template <class ThreadingPolicy>
    class Foo: public ThreadingPolicy {
    public:
        typedef typename ThreadingPolicy::ScopedLock ScopedLock;
        using ThreadingPolicy::lock_;
        void bar() {
            ScopedLock lock(lock_);
            cout << "Bar!" << endl;
        }
    };
    
    int main(int argc, char *argv[])
    {
        Foo<BoostThreaded> my_threaded_foo;
        my_threaded_foo.bar();
    
        Foo<QThreaded> my_qthreaded_foo;
        my_qthreaded_foo.bar();
    
        Foo<SingleThreaded> my_singlethreaded_foo;
        my_singlethreaded_foo.bar();
    
        return 0;
    }
    

    Meinungen ?


  • Administrator

    Wieso erbst du von der Policy?

    Grüssli



  • Ähm, weil ich das so gelernt habe?!
    Laut Alexandrescu ist policy-based design die Kombination von Mehrfachvererbung mit Templates durch das Erben von seinen Templateparametern. Wikipedia sieht das genauso.

    Philipp

    EDIT: Und so klappts auch mit der Nachbarin, ähh, der Wait-Condition:

    #include <iostream>
    #include <boost/thread.hpp>
    #include <QMutexLocker>
    #include <QWaitCondition>
    using namespace std;
    
    class BoostThreaded {
    public:
        typedef boost::mutex Mutex;
        typedef boost::mutex::scoped_lock ScopedLock;
        typedef boost::condition_variable WaitCondition;
        void wait(ScopedLock& lock) { condition_.wait(lock); }
        void notify() { condition_.notify_one(); }
    protected:
        Mutex lock_;
        WaitCondition condition_;
    };
    
    class QThreaded {
    public:
        typedef QMutex Mutex;
        typedef QMutexLocker ScopedLock;
        typedef QWaitCondition WaitCondition;
        void wait(ScopedLock&) { condition_.wait(lock_); }
        void notify() { condition_.wakeOne(); }
        QThreaded():lock_(new QMutex) {};
        ~QThreaded() { delete lock_; }
    protected:
        Mutex* lock_;
        WaitCondition condition_;
    };
    
    class SingleThreaded {
    public:
        typedef void* Mutex;
        typedef void* ScopedLock;
        void wait(ScopedLock&) {}
        void notify() {}
    protected:
        Mutex lock_;
    };
    
    template <class ThreadingPolicy>
    class Foo: public ThreadingPolicy {
    public:
        typedef typename ThreadingPolicy::ScopedLock ScopedLock;
        using ThreadingPolicy::lock_;
        using ThreadingPolicy::wait;
        using ThreadingPolicy::notify;
        void bar() {
            ScopedLock lock(lock_);
            cout << "Bar!" << endl;
            wait(lock);
            notify();
        }
    };
    
    int main(int argc, char *argv[])
    {
        Foo<BoostThreaded> my_threaded_foo;
        my_threaded_foo.bar();
    
        Foo<QThreaded> my_qthreaded_foo;
        my_qthreaded_foo.bar();
    
        Foo<SingleThreaded> my_singlethreaded_foo;
        my_singlethreaded_foo.bar();
    
        return 0;
    }
    


  • Dravere schrieb:

    Wieso erbst du von der Policy?

    Erben muß nicht sein, und eigentlich denke ich, daß es hier böse ist.
    Aber die "Empty Member" Optimization http://www.cantrip.org/emptyopt.html greift da so schön.



  • Aber wie soll ich sonst an die mutex-membervariable drankommen? Dann müsste ich ja Foo zum friend jeder Policy machen, bzw die Policy selbst wieder zum template, damit T ihr friend sein kann, damit er auf mutex_ zugreifen kann.



  • PhilippM schrieb:

    Aber wie soll ich sonst an die mutex-membervariable drankommen? Dann müsste ich ja Foo zum friend jeder Policy machen, bzw die Policy selbst wieder zum template, damit T ihr friend sein kann, damit er auf mutex_ zugreifen kann.

    Policies sind so klein und simpel, da dard ein Attribut auch mal pubnlic sein.



  • na dann 🙂



  • Dann präsentiere ich hier die parametrierbare concurrent-queue:

    #include <iostream>
    #include <queue>
    #include <boost/thread.hpp>
    #include <QMutexLocker>
    #include <QWaitCondition>
    using namespace std;
    
    class BoostThreaded {
    public:
        typedef boost::mutex Mutex;
        typedef boost::mutex::scoped_lock ScopedLock;
        typedef boost::condition_variable WaitCondition;
        void wait(ScopedLock& lock) { condition_.wait(lock); }
        void notify() { condition_.notify_one(); }
        Mutex mutex_;
        WaitCondition condition_;
    };
    
    class QThreaded {
    public:
        typedef QMutex Mutex;
        typedef QMutexLocker ScopedLock;
        typedef QWaitCondition WaitCondition;
        void wait(ScopedLock&) { condition_.wait(mutex_); }
        void notify() { condition_.wakeOne(); }
        QThreaded():mutex_(new QMutex) {};
        ~QThreaded() { delete mutex_; }
        Mutex* mutex_;
        WaitCondition condition_;
    };
    
    class SingleThreaded {
    public:
        typedef void* Mutex;
        typedef void* ScopedLock;
        void wait(ScopedLock&) {}
        void notify() {}
        Mutex mutex_;
    };
    
    template <class ThreadingPolicy>
    class Foo {
    public:
        typedef typename ThreadingPolicy::ScopedLock ScopedLock;
    
        void bar() {
            ScopedLock lock(tp.mutex_);
            cout << "Bar!" << endl;
        }
    private:
        ThreadingPolicy tp;
    };
    
    template<typename Data, class ThreadingPolicy>
    class concurrent_queue
    {
    public:
        void push(Data const& data)
        {
            {
                ScopedLock lock(tp.mutex_);
                the_queue.push(data);
            }
            tp.notify();
        }
    
        bool empty() const
        {
            ScopedLock lock(tp.mutex_);
            return the_queue.empty();
        }
    
        bool try_pop(Data& popped_value)
        {
            ScopedLock lock(tp.mutex_);
            if(the_queue.empty())
            {
                return false;
            }
    
            popped_value = the_queue.front();
            the_queue.pop();
            return true;
        }
    
        void wait_and_pop(Data& popped_value)
        {
            ScopedLock lock(tp.mutex_);
            while(the_queue.empty())
            {
                wait(mutex_);
            }
    
            popped_value=the_queue.front();
            the_queue.pop();
        }
    private:
        typedef typename ThreadingPolicy::ScopedLock ScopedLock;
        std::queue<Data> the_queue;
        ThreadingPolicy tp;
    };
    
    int main(int, char **)
    {
        Foo<BoostThreaded> my_threaded_foo;
        my_threaded_foo.bar();
    
        Foo<QThreaded> my_qthreaded_foo;
        my_qthreaded_foo.bar();
    
        Foo<SingleThreaded> my_singlethreaded_foo;
        my_singlethreaded_foo.bar();
    
        concurrent_queue<float, BoostThreaded> my_concurrent_float_queue;
        my_concurrent_float_queue.push(23.42f);
    
        float f;
        my_concurrent_float_queue.wait_and_pop(f);
        cout << f << endl;
    
        return 0;
    }
    

Anmelden zum Antworten