1

i have a class that has a method that needs to be running continuously but also be able to receive input from user. So i thought i would make the method run separately using a thread.

the code looks something like this(just the backbone):

class SystemManager
{
private:
    int command;
    bool commandAcK;

    bool running;

    //other vars

public:

    SystemManager()
    {
        //initialisation
    }

    void runningAlgorithm()
    {

        while (running)
        {
            if (commandAcK)
            {
                //solve command
            }

            //run algorithm
            //print results
        }

    }


    void readCmd()
    {

        cin >> command;
        commandAcK = true;

    }



};




int main()
{

    SystemManager *SM = new SystemManager;

    thread tRunning = SM->runningAlgorithm();


}

now the errors look like this:

no suitable constructor exists to convert from "void" to "std::thread"

Error C2440 'initializing': cannot convert from 'void' to 'std::thread'

i have found a new method and it doesn't give me any errors

std::thread tRunning(&SystemManager::runningAlgorithm, SystemManager());    

the first thing i don't understand is that this method doesn't use an instance of the class just the generic function. How can i link it to a specific instance? I need it so it can read the values of the variables.

Secondly what does "&" in front of SystemManager do?

(&SystemManager::runningAlgorithm)

Thirdly is there a better way of doing it? Do you have any ideas?

Thank you in advance.

Undefined Behaviour
  • 729
  • 2
  • 8
  • 27
Awesome
  • 47
  • 1
  • 10

4 Answers4

2

std::thread tRunning(&SystemManager::runningAlgorithm, SystemManager()); does use an instance of your class. The instance it uses is SystemManager() which is a temporary and only available to the thread. If you need to share the instance then you need to create one yourself and pass it by reference to the thread like

SystemManager sys_manager;
std::thread tRunning([&](){sys_manager.runningAlgorithm();});

And now your call site and your thread have the same instance.

Also note that command and commandAck need to be protected by some sort of synchronization since you can write to them while reading causing a data race and subsequently undefined behavior. Using std::atmoic should work for you.

NathanOliver
  • 171,901
  • 28
  • 288
  • 402
  • +1 for the synchronization, but why a lambda? Why building a closure when you already have everything you need there? – marco6 Sep 08 '16 at 12:28
  • @marco6 I used the lambda as for me it is an easy way to bind a reference to the function. I can't quite remember how to do it using bind or the thread constructor but I know this works so it is what I do. I also think it is more expressive of exactly what I want to happen. – NathanOliver Sep 08 '16 at 12:29
  • sure! Just one thing: to remember, if sys_manager goes out of scope with the other thread still running, it will be a real mess... – marco6 Sep 08 '16 at 12:31
  • 1
    @marco6 Of course there is still that. I would use a `std::shared_ptr` if the guarantee needs to be put in place. Really though it is only needed if `std::thread` is passed out of the scope. if it is not then the thread will end before `sys_manger` is destroyed and you have no problem(thanks scope and order of construction/destruction rules). – NathanOliver Sep 08 '16 at 12:34
  • I wouldn't let a beginner write lambda's like that (`[&]` or `[=]` can be dangerous). – rustyx Sep 08 '16 at 12:34
  • @RustyX What is dangerous about `[=]`? – NathanOliver Sep 08 '16 at 12:35
  • @NathanOliver poor performance of course (can accidentally deep-copy a huge map, for example). – rustyx Sep 08 '16 at 12:36
  • @RustyX OK. I thought you were talking about this example, not the general case. IMHO in this case I see nothing wrong with using a reference unless the thread is going to leave the scope. – NathanOliver Sep 08 '16 at 12:40
1

The constructor for std::thread accepts a functor, and optionally it's arguments. A functor is anything that can be "called" using operator().

Then it starts a thread and inside that thread calls your functor.

std::thread tRunning(&SystemManager::runningAlgorithm, SystemManager());  

This will call the member function SystemManager::runningAlgorithm, passing in the only argument being this (SystemManager() creates a temporary instance). Remember that member functions always accept this as the first argument.

&SystemManager::runningAlgorithm returns the address of the member function runningAlgorithm from the class SystemManager.

In modern C++ this code can be simplified (i.e. made more readable) with a lambda:

std::thread tRunning([]{ SystemManager().runningAlgorithm(); });
rustyx
  • 80,671
  • 25
  • 200
  • 267
1

The line

thread tRunning = SM->runningAlgorithm(); 

takes the result of running SM->runningAlgorithm() (a void), and tries to construct a thread from it. If you look at the relevant constructor, though, you can see it needs a function-like argument (with possibly arguments).

One way of running it is through a lambda function:

thread tRunning(
    [SM](){SM->runningAlgorithm();});

Two other things to note:

  1. You should join the thread before its destructor is called, in this case:

    tRunning.join();
    
  2. You have a (short lived) memory leak. Why not just create it on the stack?

    SystemManager SM;
    
    thread tRunning(
        [&](){SM.runningAlgorithm();});
    
    tRunning.join();
    
Ami Tavory
  • 74,578
  • 11
  • 141
  • 185
1

Uhm... I guesss you need to study some of the basic concepts of c++, before going multithread.

However... In your code,

    thread tRunning = SM->runningAlgorithm();

tries to put the result of your function (that is void... ) inside a variable of type thread... Non likely to be right.

Instead, your second code takes 2 arguments:

std::thread tRunning(
        &SystemManager::runningAlgorithm, //a pointer to a method (a pointer to the code of your function, and that is why you use the "&", even though you could have left that out)
        SystemManager()); // An instance of the value, built on the stack.

I guest that you are confused by the lack of the word "new" (coming from higher level language?), but that's how it works here:

SystemManager sm = SystemManager(); // <- variable created on the stack, will be automatically destroyed when out of scope
SystemManager *psm = new SystemManager(); // Created in the heap, while in the stack remains just a pointer to it. 
//You will need to delete it when done with :
delete psm;

To answer the question

How can i link it to a specific instance? I need it so it can read the values of the variables.

You can do:

int main()
{

    SystemManager SM; // = SystemManager(); // <- this is not needed

    std::thread tRunning(SystemManager::runningAlgorithm, SM);
    // Access SM as you need

    // REMEMBER TO CLOSE & JOIN THE THREAD!
    tRunning.join();
}

I still think you should first get used to the underlying concepts or it will be really difficult to go on.

marco6
  • 352
  • 2
  • 12