So I am supposed to create a program which computes the average of n consecutive numbers 1...n and compute it in parallel mode using t threads. I decide to use pthread.h, and so far, everything is working fine, the program compiles fine, but it is producing random results:
#include <stdio.h>
#include <stdlib.h>
#include <pthread.h>
#include <string.h>
#define INPUT_SIZE 1000
#define NUM_THREADS 5
#define ARR_SIZE INPUT_SIZE / NUM_THREADS
struct sum_params {
int size;
int* data;
};
int sum_array(struct sum_params* pt) {
int size = pt->size;
int* nums = pt->data;
int sum = 0;
for (int i = 0; i < size; i ) {
sum = nums[i];
}
return sum;
}
int main(int argc, char ** argv) {
int arr_size = ARR_SIZE;
if (INPUT_SIZE % NUM_THREADS != 0) {
arr_size ;
}
int arr[NUM_THREADS][arr_size];
int n = 1;
for (int i = 0; i < NUM_THREADS - 1; i ) {
for (int j = 0; j < arr_size; j ) {
arr[i][j] = n;
n ;
}
}
int rem = INPUT_SIZE - arr_size*(NUM_THREADS-1);
for (int j = 0; j < rem; j ) {
arr[NUM_THREADS-1][j] = n;
n ;
}
for (int j = 0; j < (arr_size-rem); j ) {
arr[NUM_THREADS-1][rem j] = 0;
}
pthread_t threads[NUM_THREADS];
for (int i = 0; i < NUM_THREADS; i ) {
struct sum_params params;
params.size = arr_size;
for (int j = 0; j < arr_size; j ) {
params.data[j] = arr[i][j];
}
pthread_create(&threads[i], NULL, (void* (*)(void*))(void*) sum_array, (void*) ¶ms);
}
int total = 0;
for (int i = 0; i < NUM_THREADS; i ) {
int thread_sum;
pthread_join(threads[i], (void*) &thread_sum);
printf("Thread Sum %d: %d\n", i, thread_sum);
total = thread_sum;
}
double average = ((double) total) / INPUT_SIZE;
printf("%f\n", average);
pthread_exit(NULL);
return 0;
}
My output:
Output 1:
Thread Sum 0: 60100
Thread Sum 1: 140100
Thread Sum 2: 140100
Thread Sum 3: 180100
Thread Sum 4: 180100
700.500000
Output 2:
Thread Sum 0: 100100
Thread Sum 1: 140100
Thread Sum 2: 140100
Thread Sum 3: 180100
Thread Sum 4: 180100
740.500000
Output 3:
Thread Sum 0: 20100
Thread Sum 1: 140100
Thread Sum 2: 140100
Thread Sum 3: 180100
Thread Sum 4: 180100
660.500000
It seems like what is happening is two threads are processing the same sub-array, i.e. on my last sample output, Threads #2 and #3 processed the sub-array [601,602,...800] when only the 3rd thread should've processed this subarray. I'm really confused why my threads are behaving oddly ? Any ideas on how to fix this? Thanks for help!
CodePudding user response:
Your thread arguments are not correct, and their lifetime are not preserved for the thread to consume. You have a significant race condition as a result. Alternatives:
- You can dynamically allocate each thread structure, send it to the thread, then let the thread consume and destroy it. Conveying the data back to the caller becomes interesting, and formidable.
- You can set aside NUM_THREADS structures, basically one for each thread, and send each thread its own, which you then enumerate.
The latter of these is easy, and shown below. This code runs the same summation for an ascending number of threads, starting with two and finishing with eight. In each case the reported thread accumulations will differ, but the resulting average should always be the same (and it is):
#define _POSIX_C_SOURCE 200809L
#include <stdio.h>
#include <pthread.h>
#define INPUT_SIZE 1000
#define MIN_THREADS 2
#define MAX_THREADS 8
struct sum_params
{
int size; // [in] size of the partition
int *data; // [in] base address of partition data
int sum; // [out] sum of partition values
};
// thread function
void *sum_array(void *pv)
{
struct sum_params *pt = pv;
pt->sum = 0;
for (int i = 0; i < pt->size; i)
pt->sum = pt->data[i];
pthread_exit(pv);
}
int main()
{
// load the array with sequence 1..INPUT_SIZE
int arr[INPUT_SIZE];
for (int i = 0; i < INPUT_SIZE; i)
arr[i] = i 1;
for (int NUM_THREADS = MIN_THREADS; NUM_THREADS <= MAX_THREADS; NUM_THREADS)
{
// determine partition size
int part_size = INPUT_SIZE / NUM_THREADS;
int part_rem = INPUT_SIZE % NUM_THREADS;
// each thread will get their own structure
struct sum_params sp[NUM_THREADS];
int *data = arr;
for (int i = 0; i < NUM_THREADS; i)
{
// if there is a remainder of < NUM_THREADS
// add one more item to each thread to soak
// up that remainder
sp[i].size = part_size;
if (part_rem)
{
sp[i].size;
--part_rem;
}
sp[i].data = data;
data = sp[i].size;
}
// start the threads.
pthread_t thrds[NUM_THREADS];
for (int i = 0; i < NUM_THREADS; i)
{
pthread_create(thrds i, NULL, sum_array, sp i);
}
// join the threads and accumulate total
int total = 0;
for (int i = 0; i < NUM_THREADS; i)
{
pthread_join(thrds[i], NULL);
printf("Thread %d total: %d\n", i, sp[i].sum);
total = sp[i].sum;
}
printf("Average: %f\n\n", (double)total / INPUT_SIZE);
}
return 0;
}
Output
Thread 0 total: 125250
Thread 1 total: 375250
Average: 500.500000
Thread 0 total: 55945
Thread 1 total: 166833
Thread 2 total: 277722
Average: 500.500000
Thread 0 total: 31375
Thread 1 total: 93875
Thread 2 total: 156375
Thread 3 total: 218875
Average: 500.500000
Thread 0 total: 20100
Thread 1 total: 60100
Thread 2 total: 100100
Thread 3 total: 140100
Thread 4 total: 180100
Average: 500.500000
Thread 0 total: 14028
Thread 1 total: 41917
Thread 2 total: 69806
Thread 3 total: 97695
Thread 4 total: 124749
Thread 5 total: 152305
Average: 500.500000
Thread 0 total: 10296
Thread 1 total: 30745
Thread 2 total: 51194
Thread 3 total: 71643
Thread 4 total: 92092
Thread 5 total: 112541
Thread 6 total: 131989
Average: 500.500000
Thread 0 total: 7875
Thread 1 total: 23500
Thread 2 total: 39125
Thread 3 total: 54750
Thread 4 total: 70375
Thread 5 total: 86000
Thread 6 total: 101625
Thread 7 total: 117250
Average: 500.500000