我正在研究一个小型的战斗代码,试图学习在c ++中使用线程,但是却弄明白了一个我无法弄清楚的错误。
我使用互斥文件进行同步:
mutex mux1;
和一个线程函数:
void dueler(Player &player, Player &enemy)
{
do
{
Sleep(player.get_as());
mux1.lock();
cout << player.get_name()<< " hit " << enemy.get_name() << " for " << player.get_str() << " damage!" << endl;
enemy.dmg(player.get_str());
mux1.unlock();
} while ((enemy.ask_dead() != true) && (player.ask_dead() != true));
}
我在main中调用(完整代码:http://pastebin.com/1FBf2FCQ):
int main()
{
Player Player1("kasper", 100, 5, 200);
Player Player2("Bagger", 150, 8, 3000);
thread dueler1(dueler,Player1,Player2);
thread dueler2(dueler, Player2, Player1);
dueler1.join();
dueler2.join();
cout <<endl<< "battle is over!";
}
线程功能让玩家(http://pastebin.com/ZCTfUYiS)互相争斗:
class Player
{
public:
Player(string name_, int maxhp_, int strenght_, int attackspeed_) {
name = name_;
maxhp = maxhp_;
hp = maxhp;
strenght = strenght_;
attackspeed = attackspeed_;
isdead = false;
}
void fullhp() { hp = maxhp; }
void dmg(int dmg) {
hp -= dmg;
if (hp < 0) dead();
}
void dead() {
isdead = true;
cout << name <<" died like a pessant";
}
string get_name() { return name; }
int get_hp() { return hp; }
int get_maxhp() { return maxhp; }
int get_str() { return strenght; }
int get_as() { return attackspeed; }
bool ask_dead() {return isdead; }
private:
string name;
int maxhp;
int hp;
int strenght;
int attackspeed;
bool isdead;
};
Player2
被杀时会出现问题:
Player2
谁死了)继续运行,并且在Player1死亡之前完全忽略while
语句。 我原本期望while
结束,因为条件(player.ask_dead() != true)
应该失败。
有什么建议吗?
答案 0 :(得分:2)
您已通过放置一个互斥文件来正确完成同步,以避免对对象进行并发访问。这已经是一个非常好的开始!
问题在于,当您创建一个线程时,它会为您传递的参数创建一个私有副本(另请参阅this related SO question)
您可以通过在dueller()中添加第一个参数来轻松验证参考资料:
cout << "dueller with player " << (void*)&player << " and ennemy " << (void*)&enemy << endl;
当通过引用使用参数调用dueler()
时,引用不是指原始对象(main()
中的局部变量),而是指其克隆。
你必须使用std::ref()
来确保所有线程真正引用相同的对象:
thread dueler1(dueler, std::ref(Player1), std::ref(Player2));
thread dueler2(dueler, std::ref(Player2), std::ref(Player1));
当玩家死亡时,他的dueler()
函数很可能正在等待互斥锁。因此,当互斥体变得自由时,尽管已经死了,但他还是会造成伤害。
好的!在一个游戏中你可以争辩说玩家已经死了但仍然用这把剑跑到受害者身上......但是我们也可以通过在进入保护区时添加一个条件来修复它:
mux1.lock(); // we were waiting, a certain time
if (!player.ask_dead()) { // so first check if we are still alive
cout << player.get_name() << " hit " << enemy.get_name() << " for " << player.get_str() << " damage!" << endl;
enemy.dmg(player.get_str());
}
mux1.unlock();