Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How can I pass the index of a for loop as the argument for pthread_create

I am using a for loop to create a number of threads and passing the index i as an argument as follows:

pthread_t p[count];
for (int i = 0; i < count; i++){
    pthread_create(&p[i], NULL, &somefunc, (void*)&i);
}

Then I attempt to retrieve the value of i:

void *somefunc (void* ptr){
    int id = *(int*)ptr;
}

However, I noticed that sometimes, id in the threads will have overlapping values which I suspect is due to the index of the for loop updating before the thread is able to retrieve the value (since I passed in the pointer, as opposed to the value itself). Does anyone have any suggestions to overcome this issue without slowing down the for loop?

Thanks

like image 522
Tony Avatar asked Apr 19 '13 02:04

Tony


People also ask

How do you pass arguments to Pthread function?

Example 1 - Thread Argument Passing long taskids[NUM_THREADS]; for(t=0; t<NUM_THREADS; t++) { taskids[t] = t; printf("Creating thread %ld\n", t); rc = pthread_create(&threads[t], NULL, PrintHello, (void *) taskids[t]); ... }

What is the last argument passed to the pthread_create () function?

The last parameter of pthread_create() is passed as the argument to the function, whereas the return value is passed using pthread_exit() and pthread_join() .

Which value does pthread_create () function return if it is successfully executed?

On success, pthread_create() returns 0; on error, it returns an error number, and the contents of *thread are undefined.

Which argument of pthread_create is thread entry code?

pthread_t is the data type used to uniquely identify a thread. It is returned by pthread_create() and used by the application in function calls that require a thread identifier. The thread is created running start_routine, with arg as the only argument.


2 Answers

This is happening because once you pass a pointer to i you now have multiple threads using the same value. This causes a data race because the first thread is modifying i and your second thread is expecting it to never change. You can always allocate a temporary int and pass it to the thread function.

pthread_create(&p[i], NULL, &somefunc, new int(i));

This will allocate an integer in dynamic storage (heap) and initialize it with the value of i. A pointer to the newly allocated integer will then be passed to the thread function.

Then in the thread function you can take the value passed as you already do and then delete the int object.

void *somefunc (void* ptr){
    int id = *(int*)ptr;
    delete (int*)ptr;
}

[Suggestion: Avoid C style casts.]

like image 167
Captain Obvlious Avatar answered Sep 19 '22 21:09

Captain Obvlious


As others have said, you're passing a pointer to an object that's being modified by another thread (the parent) and accessing it without any synchronization. This is an error.

There are at least 3 solutions:

  1. Allocate (via new in C++ or malloc in C) space for a single int, and have the new thread be responsible for freeing it. This is probably the worst solution because now you have an extra failure case to handle (failure to allocate) and thus it complicates and clutters your code.

  2. Cast the integer to void * and back. This will surely work on any real-world POSIX system, but it's not "guaranteed" to work, and perhaps more annoyingly, it may incur warnings. You can avoid the warnings with an intermediate cast through uintptr_t.

  3. Instead of passing an index, pass an address:

    pthread_create(&p[i], NULL, &somefunc, &p[i]);

Then the start function can recover the index (if it needs it for anything) by subtracting p:

int id = (pthread_t *)ptr - p;
like image 36
R.. GitHub STOP HELPING ICE Avatar answered Sep 18 '22 21:09

R.. GitHub STOP HELPING ICE