2012-04-27 2 views
2

Я ошибся, выбрав std :: thread, чтобы назначить параллелизм потока. У меня нет времени изучать другой метод. Поэтому я надеюсь, что вы можете помочь мне здесь, я действительно запутался во всех этих типах мьютексов, обещаний, фьючерсов и т. Д. Отсутствие разнообразной документации/учебников удручает. Итак, я хочу, чтобы 3 потока (игроков) делали то же самое - выберите 2 случайных числа в качестве индексов для глобального 2-мерного массива, если значение ячейки равно 0, оно равно Идентификатору потока, в противном случае прекратите выполнение нить. Сделайте это в цикле, пока не останется только один поток. Мне нужно найти способ синхронизации трех потоков после каждой (до следующей) итерации, чтобы один поток не мог быть на 50-й итерации, в то время как другой включен, скажем, 30, т. Е. Поток должен дождитесь, пока все остальные завершат итерацию, прежде чем двигаться дальше. Мне нужен какой-то барьер. Я попытался использовать переменные условия, чтобы потоки спали до тех пор, пока последние финишные потоки не просветят их, но я не смог заставить их работать. Мои неудачные попытки закомментировано:C++ std :: thread synchronization in loop

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» вещь, которая будет выполнена только один поток (в зависимости от того что наступит раньше) на каждой итерации, но obiviously это не то, что я думал, что это было потому, что что приводит к некоторому странному поведению. Как это сделать? Хорошо, код может быть уродливым и все, что должно быть помещено внутри классов и методов или что-то в этом роде, но мне просто нужно получить эту работу независимо от того, насколько примитивно. Спасибо заранее!

+0

Так что, если я получу это право, вы хотите, чтобы 3 потока случайным образом выбирали элемент массива, если он не равен нулю, потоки завершаются, иначе он пытается снова, и вы хотите, чтобы все потоки синхронизировались до тех пор, пока не осталось один ? – 111111

+7

Вы не ошиблись при использовании 'std :: thread'. Вы ошиблись в том, что не понимаете основную многопоточность. – jalf

+0

Да, но если его ненулевой, но равный Id, ничего не делайте, перейдите к следующей итерации. – andrisll

ответ

1

Вы можете удалить некоторые замки с помощью atomic variables как std::atomic_int и std::atomic_bool (или std::atomic_flag.

Рядом с этим, не делают блокировки и разблокировки самостоятельно, но использовать RAII оберток для этого, как std::lock_guard. Это заблокирует указанному мьютексу до тех пор, пока сам объект блокировки не будет уничтожен (обычно, когда он выходит за рамки).

Теперь, используя эти вещи, вы значительно улучшите свой код, ваша фактическая логическая ошибка довольно проста: вы не должны сравнивать с константа 3, но до playersLeft. Обратите внимание, что как условие зависит от этого, вы также должны сигнализировать всякий раз, когда вы меняете эту переменную.

+0

Я мог бы удалить LeftMutex и FinishMutex? А не GridMutex? Поэтому я меняю int PlayerLeft, закончен с помощью std :: atomic . Но чтобы сравнивать, изменять или получать свои значения, нужно ли использовать функции store, load и compare_exchange? Наверное, нет. Можно ли поставить lock_guard перед первым оператором if if и удалить GridMutex? Разблокирует ли он себя после всего блока «if»? Да, я знал об этой ошибке игроков, просто забыл ее исправить. – andrisll

+0

Как насчет этой функции call_once? Спасибо, ты мне очень помог! – andrisll

+0

@ user1079355: Вы можете использовать большинство числовых атомных типов, как и сами целые числа, для этой цели перегружены почти все операторы, такие как назначение, '+', '++', ....Скопированная блокировка будет удалена после того, как она выйдет из сферы действия, поэтому, если вы разместите ее до того, как она будет освобождена только в конце угадывания. Если вы хотите один для if (включая сам оператор if), вы просто вводите новую область с помощью '{}'. Преимущество с защитой на основе областей - это очистка вашего беспорядка, если вы делаете такие вещи, как «бросить, сломать, вернуть». – KillianDS