我想知道如何在保持性能的同时使用命令模式线程安全。我有一个仿真,其中执行了数百亿次迭代。性能至关重要。

在此模拟中,我有一堆Moves,它们对模拟中的对象执行命令。基类如下所示:

class Move
    {
        public:
            virtual ~Move(){}

            // Perform a move.
            virtual void Perform(Object& obj) = 0;

            // Undo a move.
            virtual void Undo() = 0;
    };

我将对象传递给Perform而不是传递给构造函数的原因(如Command模式所常见)的原因是,我无力每次迭代都实例化一个新的Move对象。而是,Move的具体实现只需要使用Object,维护指向它的指针以及它在需要时的先前状态。这是一个具体实现的示例:
class ConcreteMove : public Move
    {
        std::string _ns;
        std::string _prev;
        Object* _obj;
        ConcreteMove(std::string newstring): _ns(newstring) {}


        virtual void Perform(Object& obj) override
        {
            _obj= &obj;
            _prev = obj.GetIdentifier();
            obj.SetIdentifier(_ns);
        }

        virtual void Undo()
        {
            _obj->SetIdentifier(_prev);
        }
    };

不幸的是,这使我付出了代价的是线程安全性。我想并行化循环,其中多个迭代器同时对一堆对象执行移动。但是很明显,由于我的实现方式,ConcreteMove的一个实例无法重用。

我考虑过让Perform返回一个State对象,该对象可以传递给Undo,这样可以使实现线程安全,因为它与ConcereteMove状态无关。但是,在每次迭代中创建和销毁此类对象的成本太高。

此外,该模拟具有Moves的 vector ,这是因为存储在MoveManager类中的每个迭代都可以执行多次移动,该类包含由客户端实例化的Move对象指针的 vector 。我之所以这样设置,是因为每个具体Concrete Action 的构造函数都带有参数(请参见上面的示例)。

我考虑过为MoveMoveManager写一个复制运算符,以便可以在线程之间复制它,但是我不认为这是一个正确的答案,因为那时Move对象的所有权属于MoveManager而不是客户端(仅客户端)负责一审)。同样,对于MoveManager和维护它的责任也可以这样说。

更新:如果很重要,这是我的MoveManager
class MoveManager
{
    private:

        std::vector<Move*> _moves;

    public:

        void PushMove(Move& move)
        {
            _moves.push_back(&move);
        }


        void PopMove()
        {
            _moves.pop_back();
        }

        // Select a move by index.
        Move* SelectMove(int i)
        {
            return _moves[i];
        }

        // Get the number of moves.
        int GetMoveCount()
        {
            return (int)_moves.size();
        }
};

澄清:我需要的是每个线程一个Move对象的集合。它们在每次迭代中都被重复使用,每次在不同的对象上调用Perform

有谁知道如何以线程安全的方式有效解决此问题?

谢谢!

最佳答案

线程ID的概念呢?另外,为什么不预先构造标识符字符串并将指针传递给它们呢?

class ConcreteMove : public Move
{
  std::string *_ns;
  std::vector<std::string *> _prev;
  std::vector<Object *> _obj;

  ConcreteMove(unsigned numthreads, std::string *newstring)
    : _ns(newstring),
      _prev(numthreads),
      _obj(numthreads)
  {
  }

  virtual void Perform(unsigned threadid, Object &obj) override
  {
    _obj[threadid] = &obj;
    _prev[threadid] = obj.GetIdentifier();
    obj.SetIdentifier(_ns);
  }

  virtual void Undo(unsigned threadid)
  {
    _obj[threadid]->SetIdentifier(_prev[threadid]);
  }
};

09-12 23:30