2

我选择 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_once”事物中的表达式在每次迭代中仅由一个线程(以先到者为准)执行,但显然这不是我认为的那样,因为这会导致一些奇怪的行为。怎么做?好的,代码可能很难看,应该放在类和方法之类的东西中,但无论多么原始,我都需要让它工作。先感谢您!

4

1 回答 1

1

您可以通过使用和(std::atomic_int或.std::atomic_boolstd::atomic_flag

除此之外,不要自己进行锁定和解锁,而是RAII为此使用包装器,例如std::lock_guard. 这将锁定指定的互斥体,直到锁定对象本身被销毁(通常是当它超出范围时)。

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

于 2012-04-27T17:00:02.223 回答