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;