1
votes

I'm working with a C++ class-library that provides a Thread base-class where the user has to implement a run() method.

Is there a recommended way on how to pass parameters to that run() method? Right now I prefer to pass them via the constructor (as pointers).

7

7 Answers

1
votes

I'm not sure about C++, but that's how you would do it in Java. You'd have a class that extends Thread (or implements Runnable) and a constructor with the parameters you'd like to pass. Then, when you create the new thread, you have to pass in the arguments, and then start the thread, something like this:

Thread t = new MyThread(args...);
t.start();

Must be the same in your case.

1
votes

An alternative is to extend this Thread class to accept a functor as only constructor parameter, so that you can bind any call inside it.

Then the class using threads wont need to inherit from Thread, but only have one (or more) Thread member. The functor calls any start point you want ( some method of the class with any parameters )

1
votes

Here is a typical pattern:

1) Define a data structure that encapsulates all the data your thread needs 2) In the main thread, instantiate a copy of the data structure on the heap using operator new. 3) Fill in the data structure, cast the pointer to void*, pass the void* to the thread procedure by whatever means you are provided by your thread library. 4) When the worker thread gets the void*, it reinterpret_cast's it to the data structure, and then takes ownership of the object. Meaning when the thread is done with the data, the thread deallocates it, as opposed to the main thread deallocating it.

Here is example code you can compile & test in Windows.

    #include "stdafx.h"
    #include <windows.h>
    #include <process.h>

    struct ThreadData
    {
        HANDLE isRunning_;
    };

    DWORD WINAPI threadProc(void* v)
    {

    ThreadData* data = reinterpret_cast<ThreadData*>(v);
    if( !data )
        return 0;

    // tell the main thread that we are up & running
    SetEvent(data->isRunning_);

    // do your work here...

    return 1;
}

int main()
{
    // must use heap-based allocation here so that we can transfer ownership
    // of this ThreadData object to the worker thread.  In other words, 
    // the threadProc() function will own & deallocate this resource when it's
    // done with it.
    ThreadData * data = new ThreadData;
    data->isRunning_ = CreateEvent(0, 1, 0, 0);

    // kick off the new thread, passing the thread data
    DWORD id = 0;
    HANDLE thread = CreateThread(0, 0, threadProc, reinterpret_cast<void*>(data), 0, &id);

    // wait for the worker thread to get up & running
    //
    // in real code, you need to check the return value from WFSO and handle it acordingly.  
    // Here I assume the retval is WAIT_OBJECT_0, indicating that the data->isRunning_ event 
    // has become signaled
    WaitForSingleObject(data->isRunning_,INFINITE); 

    // we're done, wait for the thread to die
    WaitForSingleObject(thread, INFINITE);
    CloseHandle(thread);

    return 0;

}
0
votes

A common problem with thread startup is that the arguments passed exist on only the stack in the calling function. Thread startup is often deferred, such that the calling function returns and it is only some time later the thread actually starts - by which time the arguments are no longer in existence.

One solution to this is to create an event and then start the thread, passing the event as one of the arguments. The starting function then waits on the event, which is signalled by the thread when it has completed startup.

0
votes

You can pass the parameters as members of the thread class. The thread which creates the thread can presumably call other methods and/or call member functions before the thread starts. Therefore it can populate whatever members are necessary for it to work. Then when the run method is called, it will have the necessary info to start up.

I am assuming that you will use a separate object for each thread.

You would normally put all the threads you create into an array, vector etc.

0
votes

It is ok to pass them via constructor. Just be sure that pointers will live longer than the thread.

0
votes

Well, I'd prefer to put the parameters in the Start() method, so you can have a protected constructor, and doesn't have to cascade the parameters through derived class constructor.

I'd prolly let my decleration look something like this:

class Thread
{
public:
   virtual void Start(int parameterCount, void *pars);
protected:
   Thread();
   virtual void run(int parameterCount, void *pars) = 0;
}

Just make sure that your parameters are somehow contracted, e.g. #1 will be int, #2 will be a double etc. etc. :)