首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >并行命令模式

并行命令模式
EN

Stack Overflow用户
提问于 2015-03-14 22:59:16
回答 3查看 385关注 0票数 3

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

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

代码语言:javascript
复制
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,维护指向它的指针,并在需要时保持以前的状态。下面是一个具体实现的示例:

代码语言:javascript
复制
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返回一个可以传递给UndoState对象,这样可以使实现线程安全,因为它独立于ConcereteMove状态。然而,在每次迭代中创建和销毁这样一个对象的代价太高了。

此外,该仿真具有一个Moves向量,因为每次迭代都可以执行存储在一个MoveManager类中的多个动作,该类包含一个由客户端实例化的Move对象指针向量。我以这种方式设置它,因为每个特定具体移动的构造函数都使用参数(参见上面的示例)。

我考虑为MoveMoveManager编写一个复制操作符,这样就可以在线程之间复制它,但是我不认为这是一个正确的答案,因为这样Move对象的所有权就落在MoveManager而不是客户端(客户机只负责第一个实例)上。同样,MoveManager和维护它的责任也是如此。

MoveManager更新:这是我的(如果它重要的话)

代码语言:javascript
复制
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澄清:我所需要的只是每个线程一个对象的集合。每次迭代都会重用它们,每次都在不同对象上调用。

有人知道如何以线程安全的方式有效地解决这个问题吗?

谢谢!

EN

回答 3

Stack Overflow用户

回答已采纳

发布于 2015-03-23 15:53:00

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

代码语言:javascript
复制
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]);
  }
};
票数 3
EN

Stack Overflow用户

发布于 2015-03-16 23:52:49

有明确的要求是不可能的。具体来说,

  1. 使用命令模式。“命令模式是一种行为设计模式,在这种模式中,对象用于表示和封装稍后调用方法所需的所有信息。”因此,您正在存储数据。
  2. 你“负担不起”分配内存。
  3. 您有“数十亿”的迭代,这意味着一些大的静态分配是不够的。

您想要存储数据,而不需要在任何地方存储数据。因此,没有答案。然而,如果你愿意改变你的需求,毫无疑问有很多方法来解决你的问题(不管它是什么-我从描述中看不出来)。

我也无法估计你一次需要多少移动对象。如果这个数字相当低,那么一个专门的分配方案可能会解决你的部分问题。同样,如果大多数移动对象都是重复的,那么不同的专门分配方案可能会有所帮助。

一般来说,你想要的东西是无法解决的,但是放松需求,这应该不难。

票数 1
EN

Stack Overflow用户

发布于 2015-03-23 20:43:50

移动管理器不应该包含指针向量,它应该是移动对象的向量

代码语言:javascript
复制
std::vector<Move> _moves;

似乎每个线程都有一个移动管理器,所以不会出现多线程问题,将向量容量设置为最大,然后将执行操作和其他操作应用到向量无新分配中,然后重用移动对象。

票数 1
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/29055249

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档