我想知道如何在保持性能的同时使用命令模式线程安全。我有一个模拟,我执行了超过数百亿次的迭代;表现至关重要。
在这个模拟中,我有一堆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
的向量,因为可以在存储在MoveManager
类中的每次迭代中执行多次移动,该类包含由客户端实例化的Move
对象指针的向量。我这样设置因为每个特定Concrete移动的构造函数都采用参数(参见上面的例子)。
我考虑为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();
}
};
澄清:我需要的是每个线程的Move
个对象集合。它们在每次迭代时重复使用,每次在不同的对象上调用Perform
。
有谁知道如何以线程安全的方式有效地解决这个问题?
谢谢!
答案 0 :(得分:3)
线程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]);
}
};
答案 1 :(得分:1)
不符合规定的要求。具体地,
您希望存储数据而无需存储任何地方。因此没有答案。但是,如果您愿意改变您的要求,毫无疑问有很多方法可以解决您的问题(不管它是什么 - 我无法从描述中说出来。)
我也无法一次估算出你需要多少个Move对象。如果该数字相当低,那么专门的分配方案可能会解决您的部分问题。同样,如果大多数Move对象都是重复的,则不同的专用分配方案可能有所帮助。
一般来说,你所提出的要求无法解决,但放宽要求并且不应该很难。
答案 2 :(得分:1)
您的移动管理器不应包含指针向量,它应该是移动对象的向量
std::vector<Move> _moves;
似乎每个线程都有一个Move Manager,因此不存在多线程问题,将向量容量设置为max,然后在向量中移动时应用执行和其他操作 没有新的分配,您将重用移动对象