C++ 并行命令模式

C++ 并行命令模式,c++,multithreading,oop,C++,Multithreading,Oop,我想知道如何在保持性能的同时使我使用的命令模式线程安全。我有一个模拟,我执行了数百亿次迭代;性能至关重要 在这个模拟中,我有一系列的移动,它们对模拟中的对象执行命令。基类如下所示: class Move { public: virtual ~Move(){} // Perform a move. virtual void Perform(Object& obj) = 0;

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

在这个模拟中,我有一系列的
移动
,它们对模拟中的对象执行命令。基类如下所示:

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

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

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

我之所以在
Perform
上传递对象,而不是像命令模式那样传递构造函数,是因为我无法在每次迭代中实例化一个新的
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
,这样实现线程安全,因为它独立于
concertemove
状态。然而,在每次迭代中创建和销毁这样的对象代价太高

此外,模拟具有
移动的向量
,因为在
MoveManager
类中存储的每个迭代都可以执行多个移动,该类包含客户机实例化的
Move
对象指针向量。我这样设置它是因为每个特定混凝土移动的构造函数都采用参数(参见上面的示例)

我考虑过为
Move
MoveManager
编写一个复制操作符,这样它就可以在线程之间复制,但我认为这不是一个正确的答案,因为
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();
        }
};
类移动管理器
{
私人:
std::vector_移动;
公众:
无效移动(移动&移动)
{
_移动。将_向后推(&move);
}
void PopMove()
{
_移动。向后弹出();
}
//按索引选择移动。
移动*选择移动(整数i)
{
返回移动[i];
}
//获取移动次数。
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]);
      }
    };
    
    类具体移动:公共移动
    {
    std::string*\n;
    标准::向量_prev;
    std::vector_obj;
    ConcreteMove(无符号numthreads,std::string*newstring)
    :_ns(新闻字符串),
    _prev(numthreads),
    _obj(numthreads)
    {
    }
    虚拟空执行(未签名的线程ID、对象和对象)覆盖
    {
    _obj[threadid]=&obj;
    _prev[threadid]=obj.GetIdentifier();
    对象设置标识符(_ns);
    }
    虚拟作废撤消(未签名的线程ID)
    {
    _obj[threadid]->SetIdentifier(_prev[threadid]);
    }
    };
    
    您的移动管理器不应该包含指针向量,它应该是移动对象向量

    std::vector<Move> _moves;
    
    std::vector\u移动;
    
    似乎每个线程都有一个移动管理器,因此不会出现多线程问题,请将向量容量设置为max,然后在向量中的移动上应用perform和其他操作
    没有新的分配,您将重用移动对象

    也许我应该澄清一下-我每个线程只需要一个移动对象(更确切地说,一个移动对象集合)。我在每次迭代中都会重复使用它们(我编辑了问题以澄清这一点).为了避免每次迭代都需要创建和销毁移动对象,我提出了上面展示的方案,在这里我传入要“移动”的对象并存储指向它的指针。这样做的结果是,我不能再跨多个线程重用移动对象-我正在寻找一个替代设计的建议。如果它不是命令模式,那么它就不是。但现在我被卡住了。如果你只需要每个线程的移动对象集合,是什么阻止你复制e每个线程的每个移动对象?“我可以
    std::vector<Move> _moves;