46

I have the following functions :

void *foo(void *i) {
    int a = (int) i;
}

int main() {
    pthread_t thread;
    int i;
    pthread_create(&thread, 0, foo, (void *) i);
}

At compilation, there are some errors about casting ((void *) i and int a = (int) i). How can I pass an integer as the last argument of pthread_create properly?

jww
  • 97,681
  • 90
  • 411
  • 885
Gradient
  • 2,253
  • 6
  • 25
  • 36

5 Answers5

55

Building on szx's answer (so give him the credit), here's how it would work in your for loop:

void *foo(void *i) {
    int a = *((int *) i);
    free(i);
}

int main() {
    pthread_t thread;
    for ( int i = 0; i < 10; ++1 ) {
        int *arg = malloc(sizeof(*arg));
        if ( arg == NULL ) {
            fprintf(stderr, "Couldn't allocate memory for thread arg.\n");
            exit(EXIT_FAILURE);
        }

        *arg = i;
        pthread_create(&thread, 0, foo, arg);
    }

    /*  Wait for threads, etc  */

    return 0;
}

On each iteration of the loop, you're allocating new memory, each with a different address, so the thing that gets passed to pthread_create() on each iteration is different, so none of your threads ends up trying to access the same memory and you don't get any thread safety issues in the way that you would if you just passed the address of i. In this case, you could also set up an array and pass the addresses of the elements.

Crowman
  • 25,242
  • 5
  • 48
  • 56
  • 1
    I get this when I compile : `error: invalid conversion from ‘void*’ to ‘int*’` at this line : `int *arg = malloc(sizeof(*arg));`. You should put (int *) before malloc. – Gradient Oct 08 '13 at 00:35
  • 2
    @Gradient: You must be compiling this as C++. The cast is not necessary in C and, to my mind, should not be included. – Crowman Oct 08 '13 at 00:38
  • I believe the statement `int *arg = malloc(sizeof(*arg));` should instead be `int *arg = malloc(sizeof(arg));`. Here it works because a pointer on most machines is the size of int. However it may fail for other data types or even custom types. Correct me if I am wrong here – AjB Feb 11 '15 at 15:36
  • 2
    @Pegasus: you are incorrect. Here you want to be allocating the correct amount of space for an `int`, not for a pointer to `int`. Your alternative would do the latter, so it's actually your suggestion that would only work if pointers and `int`s are the same size. – Crowman Feb 11 '15 at 17:48
  • But doesn't this statement `int *arg = malloc(sizeof(*arg))` in fact allocates space for a pointer rather a double pointer (consider *arg is in fact a pointer to a pointer to an int). I am still not convinced with your answer sir – AjB Feb 12 '15 at 07:49
  • 1
    @Pegasus: No, it allocates space for an `int`. `int * arg = malloc(sizeof(&arg));` would allocate space for a pointer to pointer to `int`. If you're "not convinced", then you're just confused about basic C syntax. – Crowman Feb 12 '15 at 11:57
  • I didn't mean to cause any offence. I'm just a beginner while you seem to be a veteran. Just expressed my thoughts. Nevertheless I'd like to know what `arg` means in the statement `int * arg = malloc(sizeof(&arg));`. To me it looks like an integer pointer and thus `sizeof(arg)` should obviously refer to an integer pointer. Building on that, `sizeof(&arg)` should refer to a pointer to a pointer to an int. To me this looks like were referring a pointer not to the base type which is `int`. If this is wrong, could you please elaborate a bit? Could you start with what `arg` in that statement means? – AjB Feb 12 '15 at 13:04
  • @Pegasus: Stack Overflow comments are a very poor venue to each you basic C even if I had the inclination to do so. You should invest in an elementary tutorial to go through these things. `arg` here is type `int *`, therefore `&arg` is type `int **` and `*arg` is type `int`. Since you want to allocate space for an `int`, `int * arg = malloc(sizeof(*arg));` is the correct way to do it. – Crowman Feb 12 '15 at 13:39
  • @Pegasus: No, it wasn't a typo. I clearly said that `int * arg = malloc(sizeof(&arg));` would "allocate space for a pointer to pointer to `int`", to respond to your mistaken supposition in your preceding comment that `int *arg = malloc(sizeof(*arg));` would do that, which it wouldn't. In the actual code, we don't *want* to allocate space for a pointer to pointer to `int` - we want to allocate space for an `int`. – Crowman Feb 12 '15 at 14:03
  • yeah thats why I removed my comment but you caught it anyway :). Noevertheless I've got the whole thing now. Thanks – AjB Feb 12 '15 at 14:08
  • How do I print value of a inside foo? – NSGodMode Apr 20 '15 at 02:43
  • @NSGodMode: With `printf("%d\n", a)`, since [POSIX requires `printf()` to be thread-safe](http://stackoverflow.com/a/4353741/2399879). – Crowman Apr 20 '15 at 02:47
  • In my program I have struct like this : typedef struct { int start; int end; } Range; and i want to print "start" in foo, how do I do this? – NSGodMode Apr 20 '15 at 02:54
  • @NSGodMode: Then you need to ask a new question, if you have one that doesn't relate to this question. – Crowman Apr 20 '15 at 02:56
  • Ok , I'm creating a question now. Please answer it. thanks. I'm doing an assignment due today and I need it for that. Thanks – NSGodMode Apr 20 '15 at 02:59
  • @PaulGriffiths my new question. please answer it : http://stackoverflow.com/questions/29738664/print-arguments-inside-thread – NSGodMode Apr 20 '15 at 03:02
26

You can allocate an int on the heap and pass it to pthread_create(). You can then deallocate it in your thread function:

void *foo(void *i) {
    int a = *((int *) i);
    free(i);
}

int main() {
    pthread_t thread;
    int *i = malloc(sizeof(*i));
    pthread_create(&thread, 0, foo, (void *) i);
}
szx
  • 6,433
  • 6
  • 46
  • 67
  • Like I said in another answer : I am not sure this solution work. By the time the first thread reaches `int a = *((int *) i)`, the for loop could have changed the value of `i`. Thus, when the first thread tries to initialize `a`, it would not read the right value. Or maybe I am confused with the concept of threads? – Gradient Oct 07 '13 at 22:46
  • 1
    @Gradient: Your other comment was correct, but that's not what's happening here. This solution allocates new memory for the argument to each thread (or, at least, if you put it in a loop, you would allocate new memory in each loop) so each thread gets a different object, and no two threads try to access the same memory. You're getting the concept of threads right, this solution just doesn't exhibit the problem you mentioned. I think this solution would be improved by actually showing how it would work in a loop. – Crowman Oct 07 '13 at 22:48
11

You should cast the address of i (rather than the value of i as you do now) in the last argument of pthread_create().

pthread_create(&thread, 0, foo, (void *) &i);
                                         ^  is missing

And the casting is wrong in your function too. It should be:

int a = *((int*) i);
  1. If you intend to read the value, you should also initialize i to some value in main() as it's uninitialized now.

2 Use proper definition for main():

 int main(void) 

or int main(int argc, char *argv[]) or its equivalent.

P.P
  • 117,907
  • 20
  • 175
  • 238
  • Does that work if `i` is declared in a `for` loop? For example `for (int i=0;...;...)` Or should I declared it before the `for` loop? – Gradient Oct 07 '13 at 21:13
  • Declaring in a for loop is no different & it would work the same way. But declaring variable in for loop is allowed only in C99 (or later) mode. You would need to compile with `-std=c99` for example, along with other compiler options. – P.P Oct 07 '13 at 21:22
  • Also, if I do `&i`, then I give the thread the address of variable `i`. If I create other threads, wouldn't `i` be shared between all threads? That would not be the desired behavior. – Gradient Oct 07 '13 at 21:26
  • Yes. All threads share the same address space. If sharing is a problem, you have to use locks (e.g mutex) to get exclusive access. – P.P Oct 07 '13 at 21:32
  • Ok. I have a `for` loop that creates threads. Is there a way to pass an integer as the forth argument of `pthread_create` so that each thread has its own value, independent in each thread? Or maybe I have to create a variable for each thread? – Gradient Oct 07 '13 at 21:57
  • 3
    I am not sure the second solution would work. By the time, the first thread reaches `int a = *((int *) i)`, the for loop could have changed the value of `i`. Thus, when the first thread tries to initialize `a`, it would not read the right value. – Gradient Oct 07 '13 at 22:06
  • 1
    @Gradient: You are correct, passing the address of the loop variable is not guaranteed to be safe. The results of casting an `int` to a pointer and back are implementation-defined, so this is not a great solution, either. Using `malloc()` is the best way. – Crowman Oct 07 '13 at 22:47
8

Old question, but I faced the same problem today, and I decided not to follow this path. My application was really about performance, so I chose to have this array of ints declared statically.

Since I don't know a lot of applications where your pthread_join / pthread_cancel is in another scope than your pthread_create, I chose this way :

#define NB_THREADS 4

void *job(void *_i) {
  unsigned int i = *((unsigned int *) _i);
}

int main () {
  unsigned int ints[NB_THREADS];
  pthread_t    threads[NB_THREADS];
  for (unsigned int i = 0; i < NB_THREADS; ++i) {
    ints[i] = i;
    pthread_create(&threads[i], NULL, job, &ints[i]);
  }
}

I find it more elegant, more efficient, and you don't have to worry about freeing since it only lives in this scope.

Jerska
  • 11,722
  • 4
  • 35
  • 54
3

While this is an old question there is one option missing when all you need is to pass a positive integer like a descriptor: you can pass it directly as the address, while it it a hack it works well and avoid allocating anything :)

NOTE: the size of the integer must match the size of a pointer on your OS but nowadays most systems are native 64bits.

#include <pthread.h>
#include <inttypes.h>
#include <stdio.h>

void *_thread_loop(void *p)
{
  uint64_t n = (uint64_t)p;

  printf("received %llu\n", n);

  return NULL;
}



int main(int argc, char const *argv[])
{
  pthread_t read_thread_id;
  uint64_t n = 42;
  pthread_create(&read_thread_id, NULL, _thread_loop, (void *)n);

  pthread_join(read_thread_id, NULL);
  return 0;
}
Schmurfy
  • 1,715
  • 9
  • 17
  • According to [this answer](https://stackoverflow.com/a/8618736/5003848), the result of this hack is implementation-defined and (generally speaking) not portable. – tonysdg Jan 16 '18 at 13:54
  • I am not entirely sure the answer you link really means that, he says the result may not point to any valid data and that is obvious (the pointer will never be dereferenced) but so far I never had issues doing that, I have no tested exotic architectures though but worked for me on linux on x86 and am64 as well as arm (on a raspberrypi). – Schmurfy Jan 28 '18 at 19:13
  • Oh, I highly doubt you'll ever have issues with it unless you're running on a *really* exotic architecture :-) And this hack has worked for me on more than a few occasions. I'm just posting for completeness! – tonysdg Jan 30 '18 at 00:08