【发布时间】:2010-08-14 06:53:33
【问题描述】:
我目前正在学习如何在 C++ 中进行多线程处理。我的一个学习项目是俄罗斯方块游戏。在这个项目中,我有一个包含所有游戏状态数据的 Game 类。它有移动方块的方法和其他一些东西。用户将访问此对象(用户将使用箭头键从主线程移动块),同时线程计时器正在对活动块执行重力(定期降低它)。
起初我认为我可以通过添加互斥成员变量并将其锁定在每个方法调用中来使 Game 类线程安全。但问题在于它只保护单个方法调用,而不是涉及多个方法调用的更改。例如:
// This is not thread-safe.
while (!game.isGameOver())
{
game.dropCurrentBlock();
}
我尝试的一个解决方案是为互斥变量添加一个访问器方法以从外部锁定它:
// Extra scope added to limit the lifetime of the scoped_lock.
{
// => deadlock, unless a recursive mutex is used
boost::mutex::scoped_lock lock(game.getMutex());
while (!game.isGameOver())
{
game.dropCurrentBlock();
}
}
但是,除非使用递归互斥锁,否则这将死锁。现在,看看 StackOverflow 上的 some posts,似乎大多数人强烈反对使用递归互斥锁。
但是,如果递归互斥锁是一个不可选项,这是否意味着不可能创建一个线程安全的类(支持协调更改)?
唯一有效的解决方案似乎是永远不要在方法调用内部锁定互斥锁,而是始终依赖用户从外部进行锁定。
但是,如果是这种情况,那么让 Game 类保持原样并创建一个将 Game 对象与互斥锁配对的包装类不是更好吗?
更新
我尝试了包装器的想法,并创建了一个名为 ThreadSafeGame (cpp) 的类,如下所示:
class ThreadSafeGame
{
public:
ThreadSafeGame(std::auto_ptr<Game> inGame) : mGame(inGame.release) {}
const Game * getGame() const
{ return mGame.get(); }
Game * getGame()
{ return mGame.get(); }
boost::mutex & getMutex() const
{ return mMutex; }
private:
boost::scoped_ptr<Game> mGame;
mutable boost::mutex mMutex;
};
// Usage example, assuming "threadSafeGame" is pointer to a ThreadSafeGame object.
{
// First lock the game object.
boost::mutex::scoped_lock lock(threadSafeGame->getMutex());
// Then access it.
Game * game = threadSafeGame->getGame();
game->move(Direction_Down);
}
它有同样的缺点,它依赖于用户从外部锁定互斥锁。但除此之外,这对我来说似乎是一个可行的解决方案。
我做得对吗?
【问题讨论】:
-
有趣的是,您刚刚发现为什么 STL 容器不是“线程安全的”,锁定单个操作几乎毫无意义:)
-
您应该在
while内添加一个额外的块,其中包括除sleep之外的所有代码。你真的不想在拿着锁的时候睡觉。 -
@Davind 绝对是个好点 :) 在我的真实代码中,我使用的是计时器,所以不需要睡觉。
标签: c++ multithreading design-patterns