首页 > 解决方案 > 如何发出一个线程正确结束的信号

问题描述

所以我正在做一个学校项目来模拟实时自动战斗。代码是这样的:

void attack(Fighter& f1, Fighter& f2)
{
    while (!f1.is_down()&&!f2.is_down())
    {
        this_thread::sleep_for(chrono::seconds(f1.get_spd())/10);
        cout << f1.get_name() << " dealt " << f2.get_dmg(f1) << " damage" << endl;
    }
    if (f2.is_down()) cout << '\t' << f2.get_name() << "is defeated" << endl;
}
void Fight::fight_start()
{
    thread f1_attack(attack, f1, f2);
    thread f2_attack(attack, f2, f1);
    f1_attack.join();
    f2_attack.join();
}

战斗应该在一方被击败时结束,但他们会继续战斗直到双方都失败。如何阻止被击落的战斗机继续战斗?

编辑:我修好了。

标签: c++

解决方案


在 C++ 中,如果一个线程修改数据,任何读取器都必须以某种方式同步。如果不是,您的程序的行为不是由 C++ 标准定义的,这很糟糕。

我认为您在这里使用线程的方式在某些方面是不好的。

  1. 您的访问权限是 UB,或者您正在使用每个对象的锁定。第一个意味着你的程序被彻底破坏了;第二种是一种不组合的多线程编程。(不组合意味着两个“正确”的代码,当连接时,就问题而言可能是不正确的。)

  2. 您正在花费一个昂贵的线程作为美化的计时器。

  3. 您正在从多个线程执行非同步 io。

  4. 即使您对每个方法都进行了互斥锁定,您的线程代码也会被破坏。例如,死去的战士可以攻击。

我会在这里做的是:

制作一个调度器。它有一个排序的双端队列或多映射(关键是时间)要做的任务。当您添加任务时,您会说您希望它们何时弹出。当你等待任务时,它会让你等到有一个时间到了。

也许是这样的:

struct scheduler{
  using clock=std::chrono::steady_clock;
  using time_point=clock::time_point;
  using task=std::function<void(time_point)>;
  task pop();
  void push(time_point, task);
  void push_now(task){ push(clock::now(), std::move(task)); }
private:
  mutable std::mutex m;
  std::condition_variable cv;
  std::multimap<clock::time_point, task> queue;
};

你的战士“线程”不再是线程。

scheduler fight;
fight.push_now([&](auto when){
  round(fight, when, f1, f2);
});
fight.push_now([&](auto when){
  round(fight, when, f2, f1);
});
while(auto f=fight.pop()){
  f();
}

回合可能是:

void round(scheduler& fight, scheduler::time_point when, fighter& attacker, fighter& defender){
  if (!attacker.is_down()&&!defender.is_down()){
   std::cout<<attacker.name()<< " hits "<< defender.name() << " for " << attacker.do_dmg(defender) <<"\n";
  }
  if (defender.is_down()){
    std::cout<<defender.name()<<" defeated.";
  }
  if (attacker.is_down()){
    std::cout<<attacker.name()<<" defeated.";
  }
  if Iattacker.is_down()||defender.is_down())
    fight.push_now({});// end
  else
    fight.push(when+chrono::seconds(f1.get_spd()/10.), [&](auto when){round(fight, when, attacker, defender);});
}

如果需要,调度程序可以在线程中运行。

可能有错别字。而且您必须研究如何使用条件变量休眠,直到超时或推送新内容的通知。


推荐阅读