【问题标题】:Still having race condition with boost::mutex仍然有 boost::mutex 的竞争条件
【发布时间】:2011-06-05 06:22:08
【问题描述】:

我正在尝试一个示例,它会导致竞争条件应用互斥锁。但是,即使使用互斥体,它仍然会发生。怎么了?这是我的代码:

#include <iostream>
#include <boost/thread.hpp>
#include <vector>
using namespace std;
class Soldier
{
private:
  boost::thread m_Thread;
public:
  static int count , moneySpent;
  static boost::mutex soldierMutex;   
  Soldier(){}
  void start(int cost)
  {
    m_Thread = boost::thread(&Soldier::process, this,cost);
  }

  void process(int cost)
  {
    {
    boost::mutex::scoped_lock lock(soldierMutex);
    //soldierMutex.lock();
    int tmp = count;
    ++tmp;
    count = tmp;
    tmp = moneySpent;
    tmp += cost;
    moneySpent = tmp;  
   // soldierMutex.unlock();
    }
  }  

  void join()
  {
    m_Thread.join();
  }
};

int Soldier::count, Soldier::moneySpent;
boost::mutex Soldier::soldierMutex;

int main()
{
  Soldier s1,s2,s3;
  s1.start(20);
  s2.start(30);
  s3.start(40);
  s1.join();
  s2.join();
  s3.join();
  for (int i = 0; i < 100; ++i)
    {
      Soldier s;
      s.start(30);
    }
  cout << "Total soldier: " << Soldier::count << '\n';
  cout << "Money spent: " << Soldier::moneySpent << '\n';
}

【问题讨论】:

  • 只是出于好奇......你为什么不直接做count++moneySpent += cost;
  • tmp 在该块中的目的是什么?你应该摆脱它。
  • 为什么不等待循环中启动的线程完成?
  • @littleadv:我认为您发现了他的错误...也许您应该将其作为答案:-)
  • @iammillind:这就是它在低级别发生的方式。复制到寄存器,增加它,然后复制回具有变量的内存单元。我只是想演示这个过程。此外,我想要更多的代码来让竞争条件发生。这是故意的。

标签: c++ boost mutex boost-thread


【解决方案1】:

您似乎没有等待循环中启动的线程完成。将循环更改为:

 for (int i = 0; i < 100; ++i)
 {
   Soldier s;
   s.start(30);
   s.join();
 }

编辑以进一步解释

您看到的问题是打印出来的值是错误的,因此您假设线程中存在竞争条件。事实上,比赛是在您打印值时 - 它们是在并非所有线程都有机会执行时打印出来的

【讨论】:

  • 请注意,这会序列化线程。要并行运行它们,可以在堆上分配Soldiers 并有第二个循环加入它们,但这并不重要,因为锁无论如何都会序列化它们
  • 但是等等,互斥锁的目的不是为了防止竞争条件吗?如果是这样的话,那么就不应该有任何加入。
  • 你能解释一下为什么我每次创建 boost::thread 时都必须加入吗?我记得在 Java 中,加入是可选的。这里是必须的吗?据我所知,没有加入,线程仍然运行。现在我想要类似于 java 的东西,我可以在其中创建一个 Object 或 o lock 对象来锁定代码区域,因此线程将仅针对该代码块按顺序处理。
  • 加入当然是可选的,你的互斥锁保护的代码不会并行运行。但是等待线程完成与不并行执行某些代码是不同的。
  • @Amumu - 我在答案中添加了一部分来解释你不清楚的地方。
【解决方案2】:

基于此和您之前的帖子(如果您似乎还没有阅读所有答案)。您正在寻找的是某种形式的同步点,以防止 main() 线程退出应用程序(因为当主线程退出应用程序时,所有子线程都死了)。

这就是为什么你一直调用 join() 来防止 main() 线程退出,直到线程退出。由于您的使用,尽管您的线程循环不是并行的,并且每个线程都按顺序运行直到完成(因此使用线程没有真正意义)。

注意:join() 就像在 Java 中一样等待线程完成。它不会启动线程。

快速浏览一下 boost 文档表明您正在寻找的是一个线程组,它允许您在退出之前等待组中的所有线程完成。

//No compiler so this is untested.
// But it should look something like this.
// Note 2: I have not used boost::threads much.
int main()
{
    boost::thread_group               group;

    boost::ptr_vector<boost::thread>  threads;

    for(int loop = 0; loop < 100; ++loop)
    {
        // Create an object.
        // With the function to make it start. Store the thread in a vector
        threads.push_back(new boost::thread(<Function To Call>));

        // Add the thread to the group.
        group.add(threads.back());
    }

    // Make sure main does not exit before all the threads have completed.
    group.join_all();
}

如果我们回到您的示例并改造您的士兵类:

int main()
{
  boost::thread batallion;

  // Make all the soldiers part of a group.
  // When you start the thread make the thread join the group.
  Soldier s1(batallion);
  Soldier s2(batallion);
  Soldier s3(batallion);

  s1.start(20);
  s2.start(30);
  s3.start(40);

  // Create 100 soldiers outside the loo
  std::vector<Soldier>  lotsOfSoldiers;
  lotsOfSoldiers.reserve(100);  // to prevent reallocation in the loop.
                                // Because you are using objects we need to 
                                // prevent copying of them after the thread starts.

  for (int i = 0; i < 100; ++i)
  {
      lotsOfSoldiers.push_back(Solder(batallion));
      lotsOfSoldiers.back().start(30);
  }

  // Print out values while threads are still running
  // Note you may get here before any thread.
  cout << "Total soldier: " << Soldier::count << '\n';
  cout << "Money spent: " << Soldier::moneySpent << '\n';

  batallion.join_all();

  // Print out values when all threads are finished.
  cout << "Total soldier: " << Soldier::count << '\n';
  cout << "Money spent: " << Soldier::moneySpent << '\n';
}

【讨论】:

    猜你喜欢
    • 2017-12-02
    • 1970-01-01
    • 2023-03-24
    • 2021-09-05
    • 2023-01-30
    • 2011-12-20
    • 1970-01-01
    • 2022-01-23
    • 2018-10-08
    相关资源
    最近更新 更多