4

I made the following sample program to play with boost threading:

#pragma once
#include "boost\thread\mutex.hpp"
#include <iostream>

class ThreadWorker
{
public:
    ThreadWorker() {}
    virtual ~ThreadWorker() {}

    static void FirstCount(int threadId)
    {
        boost::mutex::scoped_lock(mutex_);
        static int i = 0;

        for(i = 1; i <= 30; i++)
        {
            std::cout << i << ": Hi from thread:  " << threadId << std::endl;
        }

    }

private:
    boost::mutex mutex_;
};

main class:

// ThreadTest.cpp
#include "stdafx.h"
#include "boost\thread\thread.hpp"
#include "ThreadWorker.h"

int _tmain(int argc, _TCHAR* argv[])
{
    boost::thread thread1(&ThreadWorker::FirstCount, 1);
    boost::thread thread2(&ThreadWorker::FirstCount, 2);
    boost::thread thread3(&ThreadWorker::FirstCount, 3);

    thread1.join();
    thread2.join();
    thread3.join();

    std::string input;
    std::cout << "Press <enter> to finish...\n";
    std::getline( std::cin, input );
    return 0;
}

When I run this I get the following output:

1: Hi from thread:  1
1: Hi from thread:  3
2: Hi from thread:  3
...

It looks like thread 1 gets there first and then thread 3. Isn't the scoped_lock supposed to prevent other threads from entering that section of code? Shouldn't the first thread that runs FirstCount() go to completion?

UPDATE

One thing I think is wrong with my code is this line:

boost::mutex::scoped_lock(mutex_);

I think it should be like:

boost::mutex::scoped_lock xyz(mutex_);

Once I do that, it does make the complaint about mutex_ not being static. Why it worked in the first place I'm not sure. Changing mutex_ to static does gives me a linking error:

1>ThreadWorker.obj : error LNK2001: unresolved external symbol "private: static class boost::mutex ThreadWorker::mutex_" (?mutex_@ThreadWorker@@0Vmutex@boost@@A) 1>c:\something\ThreadTest\Debug\ThreadTest.exe : fatal error LNK1120: 1 unresolved externals

Still playing with it.

User
  • 62,498
  • 72
  • 186
  • 247
  • 2
    That code does not compile. Is `mutex_` a static member in the class in real code? – David Rodríguez - dribeas Oct 14 '11 at 14:39
  • 2
    the `static int i` is essentially a shared global variable and it's not being protected (its being protected by different mutexes...) – sehe Oct 14 '11 at 14:42
  • It compiles for me in Visual Studio 2010 C++. I have experimented with making mutex_ both static and non-static. Either way the code compiles for me and either way the result is the same. – User Oct 14 '11 at 14:42
  • Why do you declare `i` as having `static` storage? What's the mutex for? **What are you trying to do?** – curiousguy Oct 14 '11 at 14:47
  • 1
    A static class function cannot access a non-static member of the class. `FirstCount` cannot access `mutex_` if the first is a static function and the second a non-static member. – David Rodríguez - dribeas Oct 14 '11 at 14:48
  • "_It compiles for me in Visual Studio 2010 C++._" are you saying that the **exact** code you posted compiles? – curiousguy Oct 14 '11 at 14:48
  • @curiousguy: yes. What error are you getting? – User Oct 14 '11 at 14:54
  • @curiousguy: I'm playing with threading this is just me testing my understanding of threading. – User Oct 14 '11 at 14:55
  • The exact code also compiles for me in VS2008. – tinman Oct 14 '11 at 15:01
  • Why do you even need a `ThreadWorker` class? – curiousguy Oct 14 '11 at 15:41
  • "_I'm playing with threading this is just me testing my understanding of threading._" Even with "just for testing" code, everything you write should have a well defined goal. **Do things for a reason.** What's the reason for the class? – curiousguy Oct 14 '11 at 15:49
  • 1
    It's up for debate whether I do. However, it does encapsulate the mutex. – User Oct 14 '11 at 15:59
  • 1
    @curiousguy Maybe his reason was that he likes the name ThreadWorker and so he wants to make an object and name it that. Maybe he is just learning threads, thinks they're cool and is playing with them to get to know how they work. Kind of like how you got all excited as a kid playing in the sandbox and stuffed a handful of sand in your mouth. When you look back at it now, you feel gross and dumb, but in that moment it was something new and exiting and it was awesome, and no one was around lecturing you to explain yourself. His question was clear. –  Mar 10 '15 at 00:57

4 Answers4

6

You have two errors:

First of all, as already noticed, mutex_ should be static also:

private:
    static boost::mutex mutex_;

and of course declare it somewhere (in a .cpp file preferably!):

boost::mutex ThreadWorker::mutex_{};

Now, why does the compiler not complain? Well, because you actually do not construct a scoped lock with argument mutex_ here:

boost::mutex::scoped_lock(mutex_);

Actually this will not call the constructor that you want, but create a (local) object mutex_ that is of type scoped_lock and is constructed by the default constructor. Hence, no compiler issues. You should change it to something like the following:

boost::mutex::scoped_lock l{mutex_};

Now the compiler should start complaining about mutex_

KillianDS
  • 16,936
  • 4
  • 61
  • 70
  • Is this a typo?: `boost::mutex::scoped_lock l{mutex_};` Why the braces? Also why braces here? `boost::mutex ThreadWorker::mutex_{};` thx – User Oct 14 '11 at 15:45
  • 1
    Not a typo, it's C++11's improved constructor syntax that prevents some common issues. I don't know if visual studio supports it already however. – KillianDS Oct 14 '11 at 15:50
  • Interesting I just tried: `int(j);` and it works declaring an integer j. I didn't know about that. Kinda scary with the `boost::mutex::scoped_lock(mutex_);` only because it will silently accept it. – User Oct 14 '11 at 16:08
3

You have three separate objects, and neither of them can see the other's mutex_ because that member is created within each object.

Perhaps you meant to make mutex_ static as well?

Edit: If I make the mutex static and remove the static from the i variable then it appears to work as I guess you meant it. Seems to be something like this happening: each thread enters the loop immediately, and are not locked from each other due to the mutex not being static. By the time they have all output to the console (I cannot remember whether there is a mutual exclusion on writing to cout) and i gets incremented they all see the static i as 30 and exit.

Edit 2: Nope, still not correct as there are still interspersed values on some runs.

Edit 3: The reason it compiles is that your scoped_lock is a temporary which seems to throw the compiler off the fact that mutex_ should be static. Try the following code instead:

#include <iostream>
#include "boost\thread\mutex.hpp"
#include "boost\thread\thread.hpp"

class ThreadWorker
{
public:
    ThreadWorker() {}
    virtual ~ThreadWorker() {}

    static void FirstCount(int threadId)
    {
        // Created object f here rather than temprary
        boost::mutex::scoped_lock f(mutex_);
        int i = 0; // Not static

        for(i = 1; i <= 30; i++)
        {
            std::cout << i << ": Hi from thread:  " << threadId << std::endl;
        }

    }

private:
    static boost::mutex mutex_; // Static
};

// Storage for static
boost::mutex ThreadWorker::mutex_;

int main(int argc, char* argv[])
{
    boost::thread thread1(&ThreadWorker::FirstCount, 1);
    boost::thread thread2(&ThreadWorker::FirstCount, 2);
    boost::thread thread3(&ThreadWorker::FirstCount, 3);

    thread1.join();
    thread2.join();
    thread3.join();

    std::string input;
    std::cout << "Press <enter> to finish...\n";
    std::getline( std::cin, input );
    return 0;
}
tinman
  • 6,348
  • 1
  • 30
  • 43
  • Thought the same thing and I tried making mutex_ static but the result is the same. – User Oct 14 '11 at 14:39
  • 1
    The reason it passes is not because the compiler optimises away a temporary (if it would do this, many RAII code would fail). It actually does not construct the temporary that you expect it to construct. – KillianDS Oct 14 '11 at 15:38
  • +1 @tinman: thanks for your help with this. Ultimately I think KillianDS nailed what was going on so I chose his as the answer – User Oct 14 '11 at 15:43
  • @KillianDS: thanks, didn't know that was valid syntax to declare a variable. – tinman Oct 14 '11 at 16:07
0

Does this code also compile with the same compiler?

Can you see the problem with this code? Try to spot the problem without compiling it.

class C {
public:
    static int f () {
        return i;
    }

    int i;
};

int main() {
    return C::f();
}

UPDATE: I just read your update.

class C {
public:
    static int f () {
        return i;
    }

    static int i;
};

int C::i = whatever;

int main() {
    return C::f();
}
curiousguy
  • 8,038
  • 2
  • 40
  • 58
0

Changed my code to be:

ThreadWorker.h:

#pragma once
#include "boost\thread\mutex.hpp"
#include <iostream>

class ThreadWorker
{ 
public:
    ThreadWorker();
    virtual ~ThreadWorker();

    static void FirstCount(int threadId);

private:
    static boost::mutex mutex_;
};

ThreadWorker.cpp:

#include "stdafx.h"
#include "ThreadWorker.h"

boost::mutex ThreadWorker::mutex_;

ThreadWorker::ThreadWorker()
{
}

ThreadWorker::~ThreadWorker()
{
}

void ThreadWorker::FirstCount(int threadId)
{
    boost::mutex::scoped_lock xyz(mutex_);
    static int i = 0;

    for(i = 1; i <= 30; i++)
    {
        std::cout << i << ": Hi from thread:  " << threadId << std::endl;
    }

}

ThreadTest.cpp:

// ThreadTest.cpp : Defines the entry point for the console application.
//

#include "stdafx.h"
#include "boost\thread\thread.hpp"
#include "ThreadWorker.h"

int _tmain(int argc, _TCHAR* argv[])
{
    boost::thread thread1(&ThreadWorker::FirstCount, 1);
    boost::thread thread2(&ThreadWorker::FirstCount, 2);
    boost::thread thread3(&ThreadWorker::FirstCount, 3);

    thread1.join();
    thread2.join();
    thread3.join();

    std::string input;
    std::cout << "Press <enter> to finish...\n";
    std::getline( std::cin, input );
    return 0;
}

The change I made was 1. separating the header and cpp file (I originally only did this to make my post more compact) 2. giving the scoped_lock variable an identifier and 3. making the mutex static.

It now works as expected, printing 30 lines from each thread sequentially. What still baffles me is why the code compiled before (as tinman was also able to compile my original code).

User
  • 62,498
  • 72
  • 186
  • 247
  • @curiousguy: The static int was intentional. I wanted to test locking a shared resource. I'm not sure what you mean about the ThreadWorker class. – User Oct 14 '11 at 15:47
  • @User: the code stopped compiling as soon as I changed the scoped_lock. As KillianDS pointed out, it was creating a new local variable not referencing the class mutex_ member. Because it never referred to the class mutex_ the compiler did not need to create code to access it from the static method and therefore there was no compiler error. – tinman Oct 14 '11 at 16:21