views:

162

answers:

5

Here is the issue that I'm having with multithreading. The proc needs to be static which means the only way I see that 2 threads can communicate and share data is through the global scope. This does not seem very clean nor does it feel very OO. I know I can create a static proc function in a class but that's still static.

What I'd like to for example do is have thread procs in the class somehow so that ex: I could create an MD5 checksum class and have an array of these objects, each on their own thread checking its hash, while the UI thread is not impaired by this and another class could simply keep track of the handles and wait for multiple objects before saying "Complete" or something. How is this limitation usually overcome?

A: 

I'm not sure I understood well... I give it a try. Are you looking for thread local storage ?

Alexandre C.
+3  A: 

You cannot avoid using a static function if you want to start a thread there. You can however (using Windows) pass the this pointer as a parameter and use it on the other side to enter the class instance.

#include <windows.h>

class Threaded {
    static DWORD WINAPI StaticThreadEntry(LPVOID me) {
        reinterpret_cast<Threaded*>(me)->ThreadEntry();
        return 0;
    }

    void ThreadEntry() {
        // Stuff here.
    }

public:
    void DoSomething() {
        ::CreateThread(0, 0, StaticThreadEntry, this, 0, 0);
    }
};
Paul
you can avoid using a static function if you use Boost (which you should if you're programming in C++)
jalf
A static trampoline is definitely the way to go. The *nix solution using pthread_create() is analogous.
Boojum
@jalf Sometimes, Boost is just not available or add a heavy dependency in a project that is undesireable.
San Jacinto
@jalf: Some people can't use boost
John Dibling
True. But when you have the option, it is by far the cleanest solution. And unless otherwise specified, I assume that Boost *can* be used. (Calling it a "heavy" dependency always puzzles me though. Most Boost libs are pretty lightweight)
jalf
@jalf it's heavy for a current project I'm on in the sense that the cross-compiler isn't compatible. For us to address all of the compatibility issues would be very difficult.
San Jacinto
+3  A: 

In C++, Boost.Thread solves the problem nicely. A thread is represented by a functor, meaning that the (non-static) operator() is the thread's entry point.

For example, a thread can be created like this:

// define the thread functor
struct MyThread {
  MyThread(int& i) : i(i) {}
  void operator()(){...}
private:
  int& i;
};

// create the thread
int j;
boost::thread thr(MyThread(j));

by passing data to the thread functor's constructor, we can pass parameters to the thread without having to rely on globals. (In this case, the thread is given a reference to the integer j declared outside the thread.)

With other libraries or APIs, it's up to you to make the jump from a (typically static) entry point to sharing non-static data.

The thread function typically takes a (sometimes optional) parameter (often of type void*), which you can use to pass instance data to the thread.

If you use this to pass a pointer to some object to the thread, then the thread can simply cast the pointer back to the object type, and access the data, without having to rely on globals.

For example, (in pseudocode), this would have roughly the same effect as the Boost example above:

void MyThreadFunc(void* params) {
  int& i = *(int*)params;
  ... 
}

int j;
CreateThread(MyThreadFunc, &j);

Or the parameter can be a pointer to an object whose (non-static) member function you wish to call, allowing you to execute a class member function instead of a nonmember.

jalf
To me, this is not solving the problem. It's the same exact problem packaged differently, because the instance data is still passed in through void *. What is the benefit? All that happens is that you've added another class to the hierarchy. This is a good portability solution, but it has little to do with the question at hand. Or am I missing something that lets you instantiate the thread and directly operate on the data?
San Jacinto
@San Jacinto: the OP assumed the only solution is to share data allocated in global scope. A `void*` passed as a parameter to the thread is not global, so it solves the OP's problem. What's more, the Boost solution preserves type safety too.
jalf
@jalf yes, I see what you mean now after reading the post again :)
San Jacinto
A: 

What's wrong with the function being static? So long as the function you've written is re-entrant, you're not violating any OO principles even if you need to pass in a pointer to the class object you're trying to read/modify.

San Jacinto
The OP's problem (as I understand it) is how to pass data to the thread without having to rely on global state
jalf
yes, I think the confusion is due to the overload of `static` as a keyword. Probably the OP meant a `static` member in the sense of C++.
Jens Gustedt
right. I do this all the time with my pthreads code. a static member function does not need to rely on _any_ global data. You just end up referencing everything by pointer.
San Jacinto
A: 

Thread creation routines usually allow you to pass a parameter to the function which will run in a new thread. This is true for both Posix pthread_create(...) and Win32 CreateThread(...). Here is a an example using Pthreads:

void* func (void* arg) {
    queue_t* pqueue = (queue_t*)arg;

    // pull messages off the queue
    val = queue_pull(pqueue);

    return 0;
}

int main (int argc, char* argv[]) {
    pthread_t thread;
    queue_t queue = queue_init();

    pthread_create(&thread, 0, func, &queue);

    // push messages on the queue for the thread to process
    queue_push(&queue, 123);

    void* ignored;
    pthread_join(&thread, &ignored);

    return 0;
}

No statics anywhere. In a C++ program you could pass a pointer to an instance of a class.

xscott
This is true, but you being to pollute the namespace (in your case the global namespace). This is why a static member function is preferred. By using one, you have a mechanism equal to what you describe without the namspace problems it creates.
San Jacinto
If you like creating classes for everything, that's fine. In C++, you could easily wrap the thread function in a namespace if that's your bag. To my taste, it's six or a half dozen.
xscott