C++ STD::线程内部的线程同步

C++ STD::线程内部的线程同步,c++,multithreading,c++11,synchronization,C++,Multithreading,C++11,Synchronization,我犯了一个错误,选择std::thread来进行线程并发分配。我没有时间学习其他方法了。因此,我希望您能在这里帮助我,我真的对所有类型的互斥、承诺、未来等感到困惑。缺乏多样的文档/教程令人沮丧。 所以我想让3个线程的玩家做同样的事情-选择2个随机数作为全局二维数组的索引,如果单元格的值为0,将其设置为线程的Id,否则停止线程的执行。在循环中执行此操作,直到只剩下一个线程。 我需要的是找到一种方法,在每次迭代之后和下一次迭代之前同步3个线程,这样一个线程就不能在第50次迭代中,而另一个线程在第30

我犯了一个错误,选择std::thread来进行线程并发分配。我没有时间学习其他方法了。因此,我希望您能在这里帮助我,我真的对所有类型的互斥、承诺、未来等感到困惑。缺乏多样的文档/教程令人沮丧。 所以我想让3个线程的玩家做同样的事情-选择2个随机数作为全局二维数组的索引,如果单元格的值为0,将其设置为线程的Id,否则停止线程的执行。在循环中执行此操作,直到只剩下一个线程。 我需要的是找到一种方法,在每次迭代之后和下一次迭代之前同步3个线程,这样一个线程就不能在第50次迭代中,而另一个线程在第30次迭代中,也就是说,线程必须等待所有其他线程完成迭代后才能继续。我需要某种屏障。 我尝试使用条件变量将线程置于睡眠状态,直到最后一个完成线程发出信号将其唤醒,但我无法使其工作。 我未成功的尝试将被注释掉:

std::mutex GridMutex, FinishMutex, LeftMutex;
int Grid[X][Y], Finished = 0, PlayersLeft = 3;

void Fight(int Id) {
  int RandX, RandY, i = 0;
  std::random_device rd; // access random device
  std::mt19937 e(rd()); // seed the engine
  std::uniform_int_distribution<int> r1(0, X-1);
  std::uniform_int_distribution<int> r2(0, Y-1);
  LeftMutex.lock(); // mutex on PlayersLeft
  while (PlayersLeft != 1) {
    ++i;
    /*std::call_once(flag, [Id, i](){*/std::cout << " Round " << i << "  Left: " << PlayersLeft << '\n';/*});*/
    LeftMutex.unlock();
//     FinishMutex.lock();
//     std::call_once(flag, [](){Finished = 0;});
//     FinishMutex.unlock();
    RandX = r1(e);
    RandY = r2(e);
    GridMutex.lock();
    if (Grid[RandX][RandY] != Id) {
      if (Grid[RandX][RandY] == 0) {
        Grid[RandX][RandY] = Id;
        std::cout << "i= "<< i << " Thread " << Id << " occupied cell " << RandX << RandY << '\n';
        std::chrono::milliseconds sleepDuration(100);
        std::this_thread::sleep_for(sleepDuration); //just to lock the grid for some time
        GridMutex.unlock();
      }
      else {
        GridMutex.unlock();
        LeftMutex.lock();
        --PlayersLeft;
        LeftMutex.unlock();
        break; //stop thread if cell occupied
      }
    }
    //Wait for the others here
//     FinishMutex.lock();
//     ++Finished;
//     if (Finished == 3) {
//       FinishMutex.unlock();
//       AllReady.notify_all();
//     }
//     else {
//       FinishMutex.unlock();
//       AllReady.wait(Lk, [](){return Finished == 3;});
//       Lk.unlock();
//     }
    LeftMutex.lock();
  }
}
int main() {
  SetGrid();
  std::thread t1(&Fight, 1);
  std::thread t2(&Fight, 2);
  std::thread t3(&Fight, 3);
  t1.join();
  t2.join();
  t3.join();
  GetGrid();
  return 0;
}
我希望call_one thing中的表达式只由一个线程执行,无论哪个线程在每次迭代中首先出现,但显然不是我所想的那样,因为这会导致一些奇怪的行为。怎么做? 好吧,代码可能很难看,所有这些都应该放在类、方法或其他东西中,但我只需要让它工作起来,不管它有多原始。
提前谢谢你

您可以使用std::atomic_int和std::atomic_bool或std::atomic_标志来移除一些锁

接下来,不要自己锁定和解锁,而是使用RAII包装器,比如。这将锁定指定的互斥锁,直到锁对象本身被销毁(通常在它超出范围时)


现在,虽然使用这些东西将大大改进代码,但实际的逻辑错误非常简单:不应该与常量3比较,而应该与playersLeft比较。请注意,由于条件取决于此,因此每当更改此变量时,您也必须发出信号。

因此,如果我做对了,您需要3个线程随机选择数组的一个元素,如果该元素不为零,则线程结束,否则它会再试一次,您希望所有线程保持同步,直到剩下一个线程为止?您在使用std::thread时没有犯错误。你犯了一个错误,没有理解基本的多线程。是的,但是如果它不为零但等于Id,那么什么也不做,跳到下一次迭代。为什么不为这样的循环任务senario尝试循环屏障同步原语呢?使用Boost中的示例,使用std::mutex、std::condition_变量很容易实现?而不是GridMutex?所以我改变了int PlayerLeft,以std::atomic结束。但是要比较、更改或获取它们的值,我必须使用存储、加载和比较交换函数吗?我想不是。我可以在第一个if语句之前放置lock\u-guard并删除GridMutex吗?它会在整个if块后自动解锁吗?是的,我知道那个playersLeft错误,只是忘了纠正它。那个一次呼叫怎么办?谢谢你,你帮了我很多忙@user1079355:您可以使用大多数数值原子类型,就像整数本身一样,几乎所有运算符都可以使用赋值、+、+、。。。为了这个目的,我们超载了。作用域锁在超出作用域后将被移除,因此,如果将其放置在if之前,它将仅在wile结束时被释放。如果希望If包含If语句本身,只需使用{}引入一个新的作用域。基于范围的防护的优点是,如果你做了一些事情,比如扔、摔、还,它会清理你的烂摊子。如果您希望每个迭代器对所有线程进行一次调用,则需要其他操作,例如,仅对一个线程将flag Controller设置为true,并仅在该标志上打印。另外,仅供参考,我让您的代码在使用2个互斥锁时不会出现死锁,尽管我认为我可以毫不费力地消除死锁,1个原子变量和1个条件变量以及一些保护锁/唯一锁。作为一个提示,您在上面发布的代码中丢失了一个notify_。想象一下:如果线程终止怎么办?