4

为了学习 boost::thread 的组合,我正在为锁定公共互斥锁 (M) 的线程实现一个简单的屏障 (BR)。但是,据我在去 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 可能会先完成...);

请不要建议复飞,例如使用我也可以使用的条件变量,但必须可以在没有它们的情况下直接进行。

4

5 回答 5

8

除了boost::lock_guard在块中限定范围之外,您还可以使用boost::unique_lock可以unlock()显式 'ed 的 a:

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

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

ownlock.unlock();

如果您需要在稍后重新获取互斥体之前释放它,这将非常有用。

至于加入,只需join()依次调用所有线程句柄即可。

于 2012-03-06T10:43:30.947 回答
4

就像是:

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();
于 2012-03-06T10:35:47.640 回答
1

让它超出范围:

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";
}
于 2012-03-06T10:35:27.127 回答
1
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,并释放任何剩余资源,如返回值。3个线程真的不值得担心。如果该内存使用是一个问题,那么您可以使用timed_join依次重复尝试所有线程。

你也可以做你不想要的——让主线程等待一个条件变量,每个线程完成后在某处存储一个值来表示它已经完成,并发出条件变量的信号,以便主线程可以join处理。您必须绝对确定线程将发出信号,否则您可能最终会永远等待它。因此,如果您取消线程,请务必小心。

于 2012-03-06T10:36:50.653 回答
1

如果你使用 boost::mutex::scoped_lock 而不是 boost::lock_guard,它有一个 unlock() 方法。如果你调用它,那么锁将不会尝试在其析构函数中重新解锁。因此,我发现代码比将锁放在自己的块中更符合我的口味。

于 2012-03-06T10:43:09.570 回答