简体   繁体   中英

Refactoring a multithreaded class into a single threaded + a multithreaded

I have a multithreaded c++ class implemented in such way:

class MyClass{
protected:
  somemutex mutex; 
  void _unsafeMethod(){...};
public:
  void safeMethod{
    locker lock(mutex);
    _unsafeMethod();
  }
}

I'm happy with that, but noticed that in most occasions, the multithreading support isn't really necessary, so I'd love to split that into 2 classes: a thread safe one, and an unsafe, but more performing one.

The issue is, there are many thousands lines of code using the class, so I need to keep interface identical. Of course, the class name will change in its definitions, depending on whether it needs to support MT or not.

I had in mind something like the following:

Solution A

class MyClass{
protected:
  void _unsafeMethod(){...};
public:
  virtual void safeMethod{
    _unsafeMethod()
  };
};
class MyThreadSafeClass: public MyClass{
protected:
  somemutex mutex; 
public:
  virtual void safeMethod{
    locker lock(mutex);
    _unsafeMethod();
  };
}

The issue here are the virtual function calls: does it makes execution slower, so I immediately lose the benefit of the single thread class performance improvement? From the first test it looks like

Solution B

class MyClass{
protected:
  somemutex * pmutex;
  void _unsafeMethod(){...};
public:
  MyClass( bool isthreadsafe ){
    if( isthreadsafe )
       pmutex = new somemutex();
    else
       pmutex = NULL;
  };
  void safeMethod{
    if( pmutex )
    {
       locker lock(*pmutex);
       _unsafeMethod();
    }
    else
      _unsafeMethod();
  };
}

This second solution looks dirty, and there's always an 'if' in each call to be resolved.

Which solution you think is more efficient? Is there a better/cleaner/more performing solution you have on mind?

Thanks a lot!

A straightforward answer would be to template MyClass on a mutex type:

template <typename MutexType>
class MyClass{
protected:
  MutextType mutex; 
public:
  void safeMethod{
    locker lock(mutex);
    // stuff
  }
}

Then you either instantiate it with a real mutex or a no-op mutex to get the behavior you desire.

You can use a decorator pattern or wrapper. .

A ]A modified wrapper so that you get away with virtual functions:

class MyClass{
protected:
  void _unsafeMethod(){};

};

class MyThreadSafeClass{
protected:
  somemutex mutex; 
  MyClass& myclassRef;
public:
    MyThreadSafeClass(MyClass& myclass):myclassRef(myclass){}
  void safeMethod(){
    locker lock(mutex);
    myclassRef._unsafeMethod();
  }
};

Class invocation:

MyClass myclass;
MyThreadSafeClass mythreadsafeclass(myclass);
mythreadsafeclass.safeMethod();

B ] A decorator with virtual interface:

class MyClassInterface{
public:
  virtual void Method()=0;

};

class MyClass : public MyClassInterface{
protected:
  virtual void Method() override{};

};

class MyThreadSafeClass: public MyClassInterface{
protected:
  somemutex mutex; 
  MyClassInterface& myclassRef;
public:
    MyThreadSafeClass(MyClassInterface& myclass):myclassRef(myclass){}
   virtual void Method() override{
     locker lock(mutex);
     myclassRef.Method();
  }
};

You can pass the mutex as a parameter to the constructor. This will allow you to select the locking mechanism without having to modify or duplicate the implementation for each locking type you wish to you.

class Mutex
{
public:
    virtual void Lock() = 0;
};

class MyClass
{
protected:
    Mutex&   mutex;

public:

    MyClass(Mutex& m) : mutex(m) {}

    void Lock()
    {
        mutex.Lock();
    }
};

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM