4

After reading this thread: How to return a value from thread in C on how to return an integer value from a pthread I tested to see if it could work for a double, but it didn't. Is there a way to return a double, long or a string from the pthread process like described in the original thread instead of returning the integer number 42?

If yes how?

And if I have a static array of 10 positions and 10 pthreads modifiying different positions each time, will I have trouble? AN example would be like "thread 0 modifies only array[0], thread 1 modifies only array[1] and so on".

Community
  • 1
  • 1
Flame_Phoenix
  • 16,489
  • 37
  • 131
  • 266
  • 2
    Steve Jessop's answer is the one you want to use. http://stackoverflow.com/a/2251472/315052 – jxh Mar 11 '13 at 23:51
  • Steve's answer is not the one I want. First because it still returns an int, second because it uses pthread_exit() to exit a pthread, which is a horrible, horrible solution and should be avoided. I have a very unique version of my code that works for integers by converting ints to uintptr_t but as far I know, there is nothing to convert from double to pointer. – Flame_Phoenix Mar 12 '13 at 00:00
  • Are you sure you want to go this route? If you're on a 64 bit system then (luckily) double and void* both have 8 bytes, so you could get all the bits returned... I'll post an answer if you insist on doing this. Probably you should just manage the data access between threads though. –  Mar 12 '13 at 01:02
  • 1
    @Flame_Phoenix: Are you not capable of changing the `int` to a `double`? You realize he's passing a pointer to an `int`, right? Not stuffing the int into a pointer's value. SO just pass a pointer to a double. And whatever work you need to do to not use `pthread_exit` (say, just let the function finish), do that. – GManNickG Mar 12 '13 at 01:03
  • 1
    Forget pthreads. Grab a modern compiler, and use C++ 11. (G++ and Clang have excellent support, and a few of the top proprietary ones have good support too.) – Nicu Stiurca Mar 12 '13 at 02:08
  • But if you insist on using pthreads, the standard way of doing it is to pass your thread a pointer to where you want it to store the result. – Nicu Stiurca Mar 12 '13 at 02:11
  • I insist on using pthreads because It is a requirement for a school project. It's that simple. As far as passing pointers, the only place where I can do that is in the pthread_create function, and I am already using it to pass a parameter. – Flame_Phoenix Mar 12 '13 at 15:20
  • @Flame_Phoenix: So what if you wanted to pass two parameters? You need to generalize your design. Pass a pointer to a general function-call structure. Most trivial would be to pass a pointer to a `std::function`, which you simply call. Then all this is as trivial as constructing a function to do what you want. – GManNickG Mar 12 '13 at 17:42

3 Answers3

4

The thread just has to dynamically allocate memory for the result you want it to return:

void *myThread(void*)
{
   double* result = (double*) malloc(sizeof(double));

   *result = 42.0;

   return result;
}

Whatever is supposed to collect that result dereferences the pointer returned using an appropriate cast (this is C with void* after all), then free the memory:

// calling thread (or whatever will collect the value)
void* thread_result;
int err = pthread_join( thread_handle, &thread_result);

double dbl = *(double*) thread_result;
free(thread_result);

As an alternative, whatever creates the thread can pass a pointer to where the thread should put it's result in the void* parameter to the thread (possibly as part of a struct if the thread needs more than just that bit of information). That might allow you to avoid dynamic memory allocation if you have a reason to avoid that, but it might make ownership and lifetime of the data somewhat more complicated to manage.

Michael Burr
  • 333,147
  • 50
  • 533
  • 760
  • I tried your solution but it doesn't compile :S

    pi_PThreads_2.cpp: In function ‘void* process(void*)’: pi_PThreads_2.cpp:41:41: error: invalid conversion from ‘void*’ to ‘double*’ [-fpermissive]

    – Flame_Phoenix Mar 12 '13 at 15:18
  • Just try casting the `double*` to a `void*` in that line: `return (void*)result;` – sonicwave Mar 12 '13 at 15:37
  • 1
    @Flame_Phoenix: sorry, my example was in C. In a C++ source file you need to cast the return from `malloc()`: `double* result = (double*) malloc(sizeof(double));`. I've updated the answer. If both the code that is allocating and the code freeing the object being used to return the value is C++, you should probably use `new`/`delete` instead of `malloc`/`free`. – Michael Burr Mar 12 '13 at 17:17
2

These are just reformulations of Steve Jessop's C solution into C++. These toy examples (note the lack of error checking) use templates to make it obvious how to change the code to use some type other than double. For example, the double could be replaced with a class type if more than one value needed to be returned after the work is done. In practice, the base class and template would likely be removed and the work() method of MyWorker would be called directly by the invoker rather than via a virtual method.

First, using pthread:

#include <iostream>
#include <pthread.h>

class WorkerBase {
protected: virtual ~WorkerBase () {}
public:    virtual void * work () = 0;
};

template <typename T>
struct Worker : public WorkerBase { T result; };

extern "C" void *invoke_worker (void *arg) {
    return static_cast<WorkerBase *>(arg)->work();
}

struct MyWorker : public Worker<double> {
    void * work () {
        result = 4.2;
        return 0;
    }
};

int main () {
    pthread_t t;
    MyWorker w;
    pthread_create(&t, 0, invoke_worker, &w);
    pthread_join(t, 0);
    std::cout << "result: " << w.result << std::endl;
    return 0;
}

Second, using C++11 std::thread:

#include <iostream>
#include <thread>

class WorkerBase {
protected: virtual ~WorkerBase () {}
public:    virtual void work () = 0;
           static void invoke (WorkerBase *w) { w->work(); }
};

template <typename T>
struct Worker : public WorkerBase { T result; };

class MyWorker : public Worker<double> {
    void work () { result = 4.2; }
};

int main () {
    MyWorker w;
    std::thread t(MyWorker::invoke, &w);
    t.join();
    std::cout << "result: " << w.result << std::endl;
    return 0;
}

You had another question in your post that I had missed:

And if I have a static array of 10 positions and 10 pthreads modifiying different positions each time, will I have trouble?

Whether or not this gives you trouble will likely depend on the array element type and your hardware architecture. In practice, I have not observed this being a problem on x86 architectures for array elements that are aligned on machine word boundaries.

Community
  • 1
  • 1
jxh
  • 69,070
  • 8
  • 110
  • 193
  • Thanks for your long answer, but passing around structures and objects is, if I am not mistaken, quite heavier than simply writing the double into a static array (a one time access to write) once the pthread has done it's job :S – Flame_Phoenix Mar 12 '13 at 15:21
  • @Flame_Phoenix: I modified the C++11 so that it no longer passes an object. If you object to pointers being passed, then the only solution would be a global variable. – jxh Mar 12 '13 at 16:31
  • 1
    @Flame_Phoenix: You don't even have working code and you're worried about whether or not something is "heavy"? Threads themselves are way heavier than any struct you'll pass a pointer to. – GManNickG Mar 12 '13 at 17:44
  • I have a working code with a static array, which is a "not so good" solution. – Flame_Phoenix Mar 14 '13 at 15:04
1

I don't think this is too hard. I don't use malloc(size_t) from cstdlib, but just new. The following example passes an argument to the thread as struct and returns another struct as well. To show it can take any variable type I also used vectors of strings...

#include <iostream>
#include <pthread.h>
#include <vector>
#include <string.h>
#include <cstdlib>
using namespace std;

struct thread_data {
    int number;
    vector<string> strings;
    thread_data (){}
    thread_data(int nr) {
        number = nr;
        strings.push_back("aba\n");
    }
};

struct thread_return_data {
    int S;
    vector<string> R;
};

void *Thread (void *threadarg) {
    thread_return_data *R = new thread_return_data;
    thread_data * Q;
    Q = (thread_data *) threadarg;
    cout << Q->number << endl;
    R->S = 16; //random number
    R->R.push_back("14fjnv"); //random string
    R->R.push_back("28jfhn"); //random string
    pthread_exit(R);
    return 0;
}

int main() {
    thread_data A(4444); //initialize with random int
    thread_return_data *B; //is pointer
    pthread_t aThread; //is variable

    pthread_create(&aThread, NULL, Thread, (void *)&A);
    pthread_join(aThread, (void **)&B);

    cout << B->S << endl;
    cout << B->R[0] << endl;
    cout << B->R[1] << endl;
    cout << A.strings[0];
    delete B;

    return 0;
}    
Niels
  • 537
  • 5
  • 22