2

I am trying an example, which causes race condition to apply the mutex. However, even with the mutex, it still happens. What's wrong? Here is my code:

#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';
}
Amumu
  • 17,924
  • 31
  • 84
  • 131
  • 3
    Just out of curiosity... why don't you just do `count++` and `moneySpent += cost;` ? – Marlon Jun 05 '11 at 06:27
  • What is the purpose of `tmp` in that block ? You should get rid of it. – iammilind Jun 05 '11 at 06:29
  • 4
    Why aren't you waiting for the threads started in the loop to finish? – littleadv Jun 05 '11 at 06:35
  • @littleadv: I think you spotted his bug... Perhaps you should make it an answer :-) – Nemo Jun 05 '11 at 06:39
  • @iammillind: That's how it happens at low level. Copy to register, increase it, then copy back to the memory cell which has the variable. I just want to demonstrate the process. Besides, I want to have more code to let race condition happens. It is intentional. – Amumu Jun 05 '11 at 06:39
  • @Amumu: what makes you think race condition did happen? – Yakov Galka Jun 05 '11 at 06:43
  • @ybungalobill If I don't join the threads together, all the threads will modify count and moneySpent. Therefore, I use a mutex to guard, but it doesn't work for me. If I join the threads together, all of them will just wait for each other to finish (process sequentially), then what's the point of multi-threading? – Amumu Jun 05 '11 at 06:58
  • @Amumu: You didn't answer my question. Are you sure you understand what is race-condition? Your problem is that you print `cout << "Total soldier: " << Soldier::count << '\n';` before the threads you `started` in the loop have finished. – Yakov Galka Jun 05 '11 at 08:09
  • Well I'm learning thread in C++. I previous did it in Java. Java has a function call start() and join() which server different purpose. However, I have to run thread with join() seems confusing. Yes, I did understand what a race condition is, and trying to make multiple thread to modify in order to use a lock to fix. But it is still confusing me. I created another thread to clarify it: http://stackoverflow.com/questions/6241738/what-exactly-is-join-in-boost-c – Amumu Jun 05 '11 at 08:32
  • @ybungalobill the race condition may happen. Maybe I don't invoke correct function to make it happen, but a code like in process() function will produce race condition. For example, two or more threads may obtain the same count or moneySpent value, and ultimately overriding each other. The problem is that I still haven't been able to produce it. When I try to increase the the amount of threads to over 400 to make it happen, it generates thread_resource_error. What should I do? – Amumu Jun 05 '11 at 09:27

2 Answers2

7

It looks like you're not waiting for the threads started in the loop to finish. Change the loop to:

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

edit to explain further

The problem you saw was that the values printed out were wrong, so you assumed there was a race condition in the threads. The race in fact was when you printed the values - they were printed while not all the threads had a chance to execute

littleadv
  • 20,100
  • 2
  • 36
  • 50
  • 2
    Note that this serializes the threads. To run them in parallel, one could allocate the `Soldier`s on the heap and have a second loop to join them, but it doesn't matter much since the lock will serialize them anyway – bdonlan Jun 05 '11 at 06:44
  • But wait, isnt' the point of mutex is to prevent race condition? If that's the case, there shouldn't be any joining then. – Amumu Jun 05 '11 at 06:52
  • Can you explain to me why do I have to join every time I create a boost::thread? I remember in Java, joining is optional. Is it a must here? As far as I see, without joining, the thread still runs. Now I want something similar to java, where I can create an Object or o lock object to lock a region of code, so the threads will process sequentially only for that block of code. – Amumu Jun 05 '11 at 07:04
  • Joining _is_ of course optional, and the code protected by your mutex will not run in parallel. But waiting for a thread to finish is something different than not executing some code in parallel. – Gunther Piez Jun 05 '11 at 07:49
  • @Amumu - I added a portion to the answer to explain what is unclear to you. – littleadv Jun 05 '11 at 08:13
1

Based on this and your previous post (were it does not seem you have read all the answers yet). What you are looking for is some form of synchronization point to prevent the main() thread from exiting the application (because when the main thread exits the application all the children thread die).

This is why you call join() all the time to prevent the main() thread from exiting until the thread has exited. As a result of your usage though your loop of threads is not parallel and each thread is run in sequence to completion (so no real point in using the thread).

Note: join() like in Java waits for the thread to complete. It does not start the thread.

A quick look at the boost documentation suggests what you are looking for is a thread group which will allow you to wait for all threads in the group to complete before exiting.

//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();
}

If we go back to your example and retrofit your Soldier class:

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';
}
Martin York
  • 257,169
  • 86
  • 333
  • 562