Ручная разблокировка буст-блокировок? - PullRequest
4 голосов
/ 06 марта 2012

Ради изучения комбинаторики boost :: thread Я реализую простой барьер (BR) для потоков, которые блокируют общий мьютекс (M). Однако, насколько я понимаю, при переходе в BR.wait () блокировки мьютекса не снимаются, поэтому для того, чтобы все потоки достигли BR, необходимо вручную снять блокировку на M. Итак, у меня есть следующий код:

boost::barrier BR(3);
boost::mutex M;

void THfoo(int m){
    cout<<"TH"<<m<<" started and attempts locking M\n";
    boost::lock_guard<boost::mutex> ownlock(M);

    cout<<"TH"<<m<<" locked mutex\n";
    Wait_(15); //simple wait for few milliseconds

    M.unlock(); //probably bad idea
    //boost::lock_guard<boost::mutex> ~ownlock(M);
    // this TH needs to unlock the mutex before going to barrier BR

    cout<<"TH"<<m<<" unlocked mutex\n";
    cout<<"TH"<<m<<" going to BR\n";
    BR.wait();
    cout<<"TH"<<m<<" let loose from BR\n";
}

int main()  
{  
    boost::thread TH1(THfoo,1);
    boost::thread TH2(THfoo,2);
    boost::thread TH3(THfoo,3);

    TH2.join(); //but TH2 might end before TH1, and so destroy BR and M
    cout<<"exiting main TH \n";

    return 0;  
}

Принимая во внимание, что M.unlock () явно плохое решение (без использования блокировки); так как (просто) снять блокировку? Кроме того: как (должным образом) ждать в main () для завершения всех потоков? (TH2.join () плохой, потому что TH2 может закончить первым ...);

Пожалуйста, не предлагайте уходы, например с условными переменными, которые я также могу использовать, но это должно быть возможно сделать прямо без них.

Ответы [ 5 ]

8 голосов
/ 06 марта 2012

В дополнение к области действия boost::lock_guard в блоке вы также можете использовать boost::unique_lock, который может быть явно unlock() ':

boost::unique_lock<boost::mutex> ownlock(M);

cout<<"TH"<<m<<" locked mutex\n";
Wait_(15); //simple wait for few milliseconds

ownlock.unlock();

Это полезно, если вам нужно освободитьмьютекс перед повторным запросом позже.

Что касается объединения, просто вызовите join() для всех дескрипторов потоков по очереди.

4 голосов
/ 06 марта 2012

Что-то вроде:

void THfoo(int m){
  // use a scope here, this means that the lock_guard will be destroyed (and therefore mutex unlocked on exiting this scope
  {
    cout<<"TH"<<m<<" started and attempts locking M\n";
    boost::lock_guard<boost::mutex> ownlock(M);

    cout<<"TH"<<m<<" locked mutex\n";
    Wait_(15); //simple wait for few milliseconds

  }
  // This is outside of the lock
  cout<<"TH"<<m<<" unlocked mutex\n";
  cout<<"TH"<<m<<" going to BR\n";
  BR.wait();
  cout<<"TH"<<m<<" let loose from BR\n";
}

Что касается ожидания, просто вызовите join на всех дескрипторах потока (если они уже завершены, функция немедленно вернется)

TH1.join();
TH2.join();
TH3.join();
1 голос
/ 06 марта 2012

Если вы используете boost :: mutex :: scoped_lock вместо boost :: lock_guard, у него есть метод unlock ().Если вы позвоните так, замок не будет пытаться повторно разблокировать свой деструктор.Таким образом, я нахожу код более приятным, чем размещение блокировки в своем собственном блоке.

1 голос
/ 06 марта 2012
cout<<"TH"<<m<<" started and attempts locking M\n";
{
    boost::lock_guard<boost::mutex> ownlock(M);

    cout<<"TH"<<m<<" locked mutex\n";
    Wait_(15); //simple wait for few milliseconds

} //boost::lock_guard<boost::mutex> ~ownlock(M);
// this TH needs to unlock the mutex before going to barrier BR

cout<<"TH"<<m<<" unlocked mutex\n";

Пока вы join все потоки, единственная проблема, когда TH2 заканчивается первым, заключается в том, что TH1 должен завершить работу до того, как TH2 может быть "пожинен" join, и любые оставшиеся ресурсы, такие как returnценность освобождена.Не стоит беспокоиться о 3-х нитках.Если это использование памяти было проблемой, то вы могли бы использовать timed_join для повторной проверки всех потоков по очереди.

Вы также можете делать то, что вам не нужно - заставить основной поток ждать переменную условия, икаждый поток, когда он завершает работу, сохраняет где-то значение, чтобы сказать, что он завершен, и сигнализирует переменную условия, чтобы основной поток мог join его.Вы должны быть абсолютно уверены, что поток будет сигнализировать, иначе вы могли бы ждать его вечно.Так что будьте осторожны, если вы когда-либо отменяете темы.

1 голос
/ 06 марта 2012

Отпустите это из области видимости:

void THfoo(int m){
    cout<<"TH"<<m<<" started and attempts locking M\n";
    {
       boost::lock_guard<boost::mutex> ownlock(M);

       cout<<"TH"<<m<<" locked mutex\n";
       Wait_(15); //simple wait for few milliseconds
    }

    // this TH needs to unlock the mutex before going to barrier BR

    cout<<"TH"<<m<<" unlocked mutex\n";
    cout<<"TH"<<m<<" going to BR\n";
    BR.wait();
    cout<<"TH"<<m<<" let loose from BR\n";
}
...