I have a program which uses MPI Pthread. I'm stuck on implementing pthreads to share an array for read/writes. I made mock code here, which mimics the issue.
#include <iostream>
#include <unistd.h>
#include <pthread.h>
struct args {
double* array;
int start;
int stop;
double myVal;
double* row;
pthread_barrier_t* barrier;
};
void* mythread(void* arguments){
struct args* args_ = (struct args*)arguments;
double* array = args_->array;
int start = args_->start;
int stop = args_->stop;
double myVal = args_->myVal;
pthread_barrier_t* barrier = args_->barrier;
double* row = args_->row;
for(int i = start; i < stop; i ){
pthread_barrier_wait(barrier);
for(int j = 0; j < 10; j ){
double a = row[j];
int ind = i*10 j;
array[ind] = a myVal;
}
}
}
int main(){
pthread_t threads[50];
int start_ = 0;
double* array_0 = NULL;
array_0 = new double[100*10];
double* row = NULL;
row = new double[10];
pthread_barrier_t barrier;
(void)pthread_barrier_init(&barrier, NULL, 50 1);
for(int n = 0; n < 50; n ){
struct args args_;
args_.start = start_;
args_.stop = start_ 2;
start_ = start_ 2;
args_.array = &array_0[0];
args_.myVal = n;
args_.row = row;
args_.barrier = &barrier;
(void)pthread_create(&threads[n], NULL, mythread, (void*)&args_);
}
for(int i = 0; i < 2; i ){
for(int k = 0; k < 10; k ){
row[k] = i 1;
}
// usleep(100);
pthread_barrier_wait(&barrier);
}
for(int n = 0; n < 50; n ){
(void)pthread_join(threads[n], NULL);
}
// print
for(int i = 0; i < 100; i ){
for(int j = 0; j < 10; j ){
int ind = i*10 j;
std::cout << " " << array_0[ind];
}
std::cout << std::endl;
}
return 0;
}
Main spawns 50 threads. Barrier is initialized with 50 1 (to include main thread). This should sync all 51 threads on the pthread_barrier_wait() call, but the blocking wait call doesn't seem to allow the "row" array write loop to complete before releasing.
The expected result should be:
1 1 1 1 1 1 1 1 1 1
2 2 2 2 2 2 2 2 2 2
2 2 2 2 2 2 2 2 2 2
3 3 3 3 3 3 3 3 3 3
3 3 3 3 3 3 3 3 3 3
4 4 4 4 4 4 4 4 4 4
4 4 4 4 4 4 4 4 4 4
5 5 5 5 5 5 5 5 5 5
5 5 5 5 5 5 5 5 5 5
.
.
.
.
.
.
etc.
The actual output is semi-random. It completes the sequence in some threads, and in others it shows zeros, as if "row" never got filled in. Adding usleep() after writing to "row" array doesn't help either - not that I can afford to have sleep functions in my code. This leads me to believe I don't understand how pointer arrays are properly shared between threads. I'm new to C so any help appreciated.
CodePudding user response:
In your loop, you create a struct args
object, and then you pass the address of this object to pthread_create
. The object is then immediately "destroyed" at the end of the loop iteration, and a new one is created at the next iteration, however, the newly created thread still has a reference to this old "destroyed" object.
You need to make sure that the object you pass to pthread_create
persists long enough for either:
- The thread to make its own copy of it
- The thread to finish
As a very simple approach, you can instead move the declaration of _args
to outside of the loop and turn it into an array like this:
struct args args_[50];
for(int n = 0; n < 50; n ){
args_[n].start = start_;
args_[n].stop = start_ 2;
start_ = start_ 2;
args_[n].array = &array_0[0];
args_[n].myVal = n;
args_[n].row = row;
args_[n].barrier = &barrier;
(void)pthread_create(&threads[n], NULL, mythread, (void*)&args_[n]);
}
The lifetime of args_[]
is now longer than each of the threads. Alternatively you can dynamically allocate your struct args
(e.g. with new
) and consume the object in the thread (e.g. with delete
). Or if you are using C 11 or later, you can use std::shared_ptr
and std::thread
, there is some documentation about how these two behave with each other here.