I am trying to write a program which gets input files and prints included items into the screen by threads. However, last thread doesn't give any output unexpectedly. I couldn't figure out what the problem is. I am waiting for your help.
Each thread gets 3 parameters :inputFile1, inputFile2 and targetBuf(lets say it is sequence number.) Files are sorted, I just simply try to print their union in order. Only positive numbers are printed.
Command line : merge N outfile file1 file2 file3 .... fileN
N is the number of input files.
If I give 2 input files (means 1 thread), it works. If I give 4 files, then 1st thread give output, 2nd one does not. If I give 6 input files, then 1st and 2nd threads give output but 3rd doesn't.
There are two structs in header file. I pasted them below.
//thread information
struct threadInfo{
pthread_t tid;
pthread_attr_t attr;
};
//Beginning thread input
struct beginThreadInput{
FILE **inputFile1, **inputFile2;
int targetBuf;
};
typedef struct threadInfo THREADINFO;
typedef struct beginThreadInput BEGINT_INPUT;
Main File Code:
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <pthread.h>
#include <semaphore.h>
#include <sys/types.h>
#include <errno.h>
#include <sys/mman.h>
#include <sys/stat.h>
#include <unistd.h>
#include <fcntl.h> //for O_ constants
#include <sys/stat.h>
#include <pthread.h> /* thread library */
#include "merge.h"
int main(int argc, char** argv){
int i, k, N;
/***FILE INITIALIZATION***/
FILE *output;
N = atoi(argv[1]);
output = fopen(argv[2], "w");
FILE **inputFileList = ((FILE**)malloc (N * sizeof(FILE*)));
printf("int N is %d\n", N);
for(i = 0; i<N; i++){
inputFileList[i] = fopen(argv[i + 3], "r");
}
//START THREADS
BEGINT_INPUT **threadInputList = ((BEGINT_INPUT**) malloc ( (N/2)* sizeof(BEGINT_INPUT*)));
THREADINFO **threadInfoList = ((THREADINFO**) malloc ( (N/2) * sizeof(THREADINFO*)));
for(i = 0; i< N/2 ; i++){
threadInputList[i] = (BEGINT_INPUT *) malloc (sizeof(BEGINT_INPUT));
threadInfoList[i] = (THREADINFO *) malloc (sizeof(THREADINFO));
}
pthread_t tid;
pthread_attr_t attr;
for(i = 0, k = 0; i < (N/2); i++){
threadInputList[i]->inputFile1 = &(inputFileList[k]);
threadInputList[i]->inputFile2 = &(inputFileList[++k]);
threadInputList[i]->targetBuf = i;
pthread_attr_init(&(threadInfoList[i]->attr));
pthread_create(&(threadInfoList[i]->tid), &(threadInfoList[i]->attr), runBeginningThreads, (void*)threadInputList[i]);
}
pthread_join((threadInfoList[[(N/2)-1]]->tid), NULL);
for(i = 0; i<N; i++){
fclose(inputFileList[i]);
}
}
void *runBeginningThreads(void *input){
BEGINT_INPUT *myInput = (BEGINT_INPUT *)input;
int first = -1, second = -1, iseof;
printf("Thread number %d\n", myInput->targetBuf);
while((second > -2) && (first > -2)){
//read integer values from files
if(first == -1){
iseof = fscanf(*(myInput->inputFile1), "%d", &first);
if(iseof == EOF){
first = -2; //means end of file
}
else if(first < 0)
first = -1; //means waiting for an integer
}
if(second == -1){
iseof = fscanf(*(myInput->inputFile2), "%d", &second);
if(iseof == EOF){
second = -2;
}
else if(second < 0)
second = -1;
}
//print smaller one
if((first != -1) && (second != -1)){
if(((first < second) || (second == -2)) && (first != -2)){
printf("%d\n", first);
first = -1;
}
else if(second != -2){
printf("%d\n", second);
second = -1;
}
}
}
}
The main problem might be that you only wait for one thread to finish, before you close all files and then exit from the program.
There are a few other extra complications in your program, that could probably be simplified. I mean that you use extra pointer indirection when you don't really need it.
Here's a little simpler version, not using extra pointer-to-pointers where none are needed:
struct beginThreadInput{
FILE *inputFile1, *inputFile2;
int targetBuf;
};
/* ... */
int main(int argc, char** argv){
/* ... */
BEGINT_INPUT *threadInputList = malloc ( (N/2)* sizeof(BEGINT_INPUT));
THREADINFO *threadInfoList = malloc ( (N/2) * sizeof(THREADINFO));
pthread_t tid;
for(i = 0, k = 0; i < (N/2); i++){
threadInputList[i].inputFile1 = inputFileList[k];
threadInputList[i].inputFile2 = inputFileList[++k];
threadInputList[i].targetBuf = i;
pthread_create(&(threadInfoList[i].tid), NULL, runBeginningThreads, &(threadInputList[i]));
}
for(i = 0; i< N/2 ; i++){
pthread_join((threadInfoList[i]->tid), NULL);
}
/* ... */
/* While not strictly needed, you should make it a good
habit of freeing all you allocate */
free(threadInfoList);
free(threadInputList);
return 0; /* You missed this */
}
I solve my question in the following way. As it is seen above, I open the files in main function, then pass them into corresponding threads. I have changed this way and passed the names of the files into threads and open related files in them.
Additionally, while passing files, I was making mistakes, its code is below
threadInputList[i]->inputFile1 = &(inputFileList[k]);
threadInputList[i]->inputFile2 = &(inputFileList[++k]);
This lines are run in a loop. This causes an input sequence like the following ([0,1], [1,2], [2,3], [3,4] ...) becuase I increment k only one time.However, each thread takes two of inputFiles. So the correct code is this:
threadInputList[i]->inputFile1 = &(inputFileList[k++]);
threadInputList[i]->inputFile2 = &(inputFileList[k++]);
I want to point out an issue. I fixed this code for file names, not FILE pointer variables as it is posted there. But to make it clear, I fixed the bug on a code posted there. Thanks for your help, everybody
Related
The function used here is working but as i am sending each threads to do this job the threads are overdoing or doing noofthreads time because of the for loops used for them when creating. I am out of logic here what can i do so that after succesfully loading and checking the 1159999 values from multiple text files i want to store them in seperate text file.
`
#include <stdlib.h>
#include <pthread.h>
#include <stdio.h>
#include <string.h>
pthread_mutex_t lck;
typedef struct{
int start;
int end;
}Innerstruct;`
struct data{
FILE **fptr;
Innerstruct *inner;
int numberthreads;
int totalno;
};
int primecount=0;//to store prime numbers count
int nofiles=3;
void countlines(int *count,FILE **fptr,int nofiles){ //counts the no of lines of cordinates from the text file
int i;
*count = 0;
for(i=0;i<nofiles;i++){
fseek(fptr[i], 0, SEEK_SET);
char line[256]; // buffer to store each line
while(fgets(line, sizeof(line), fptr[i])){
if(strlen(line) > 1){ // only increment if line is not empty so that this doesn't count blank spaces (*count)++;
}
}
}
}
void *getstorecountprime(void *p){
FILE *fptr4;
fptr4 = fopen("PrimeNumber.txt","w");
if(fptr4==NULL){
printf("Error creating file");
}
struct data *d1 = p;
int i,j,k,num,start,end;
for(k=0;k<d1->numberthreads;k++){
start= d1->inner[k].start;
end = d1->inner[k].end;
for(j=start;j<=end;j++){
for(i=0;i<nofiles;i++){
fseek(d1->fptr[i], j, SEEK_SET);
fscanf(d1->fptr[i],"%d",&num);
if(checkprime(num)){
pthread_mutex_lock(&lck);
fprintf(fptr4,"%d\n",num);
primecount++;
pthread_mutex_unlock(&lck);
}
}
}
}
}
void main(){
//checking for any error on opening the files
FILE *fptr1,*fptr2,*fptr3;
// opening text files
fptr1 = fopen("PrimeData1.txt","r");
fptr2 = fopen("PrimeData2.txt","r");
fptr3 = fopen("PrimeData3.txt","r");
if(fptr1==NULL || fptr2==NULL || fptr3==NULL){
printf("Could not open all files");
exit(1);
}
pthread_mutex_init(&lck,NULL);
FILE *fptr[] = {fptr1,fptr2,fptr3}; //array of file pointer
int num_threads,sliceperthread,*numarray;
int i;
int totalcount = 0;//to store total numbers
pthread_t *id;
struct data d1;
countlines(&totalcount,fptr,nofiles);//counts the no of lines from txt files
numarray = malloc(totalcount*sizeof(int));//dma to store all datas in array
printf("Enter how many threads do you want:");
scanf("%d",&num_threads);
id = malloc(num_threads*sizeof(pthread_t));//dma thread equal to the number of thread
d1.inner = malloc(num_threads*sizeof(Innerstruct));//dma the inner structure containing
start and end
d1.fptr = malloc(nofiles*sizeof(FILE*));//dma the fptr as array of nofiles size
sliceperthread = totalcount/num_threads;
d1.fptr=fptr;//assigned the fptr of structure to array of file pointer created above
d1.numberthreads=num_threads;\
d1.totalno = totalcount;
d1.inner[0].start = 0;
d1.inner[0].end = sliceperthread;
for(i=1;i<=num_threads;i++){
d1.inner[i].start = d1.inner[i-1].end + 1;
d1.inner[i].end = d1.inner[i-1].end + sliceperthread;
}
for(i=0;i<num_threads;i++){
pthread_create(&id[i],NULL,getstorecountprime,&d1);
pthread_join(id[i],NULL);
}
printf("%d",primecount);
}
int checkprime(int n){
int i;
for(i=2;i<=n/2;i++){
if(n%i == 0){
return 0;
}else{
return 1;
}
}
}``
As you can see from the code that i tried using a nested structure for this process. I created a structure which will store file pointers array and another inner structure array having start and end so that this could be used for threads start and end point. So i used a for loop to create threads and assign them function sending the structure as parameter. I was expecting to get prime numbers in text file but because of the iteration the program just keeps on adding values to the file.
At least these problems:
No increment
(*count)++; is lost in a comment. #M Oehm
if(strlen(line) > 1){ // only increment if line is not empty so that this doesn't count blank spaces (*count)++;
}
Wrong checkprime(int n)
Hopefully with uniform formatting it is easy to see the loop only iterates, at most, once and fails to return a value when i < 2.
int checkprime(int n) {
int i;
for (i = 2; i <= n / 2; i++) {
if (n % i == 0) {
return 0;
} else {
return 1;
}
}
}
Save time, auto format code and enable all compiler warnings to rapidly identify various code problems.
I recently got an assignment to sort members in a struct by last name and if they are the same to sort by first name. What i have so far only reads their name and age from the file but I am not properly grapsing how I would be able to sort it. So far I gathered the data from the file but im at a loss from there. I followed a code I saw but i didnt get a proper grasping of the process so i reverted back to step one.
struct Members{
int id;
char fname[50];
char lname[50];
int age;
}bio;
int main(){
int i=0;
FILE *fptr;
file = fopen("Members Bio.txt", "r");
while ( fscanf(file, "%d%s%s%d", &bio[i].id,bio[i].fname,bio[i].lname,&bio[i].age) != EOF)
{
printf("%d %s %s %d %d\n", bio[i].id,bio[i].fname, bio[i].lname, bio[i].age);
i++;
}
fclose(fptr);
}
Can anyone help me out on this one?
Code goes something like this for your case.
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
struct Members{
int id;
char fname[50];
char lname[50];
int age;
};
typedef int (*compare_func)(void*, void*);
int struct_cmp(void* s1, void* s2)
{
int l_result = strcmp(((struct Members*) s1)->lname, \
((struct Members*) s2)->lname);
if (l_result < 0)
return 1;
else if (l_result > 0)
return 0;
else
return (strcmp(((struct Members*) s1)->fname, \
((struct Members*) s2)->fname) < 0 ? 1 : 0);
}
void sort(void* arr,long ele_size,long start,long end,compare_func compare)
{
// Generic Recursive Quick Sort Algorithm
if (start < end)
{
/* Partitioning index */
void* x = arr+end*ele_size;
long i = (start - 1);
void* tmp=malloc(ele_size);
for (long j = start; j <= end - 1; j++)
{
if ((*compare)(arr+j*ele_size,x))
{
i++;
// Swap is done by copying memory areas
memcpy(tmp,arr+i*ele_size,ele_size);
memcpy(arr+i*ele_size,arr+j*ele_size,ele_size);
memcpy(arr+j*ele_size,tmp,ele_size);
}
}
memcpy(tmp,arr+(i+1)*ele_size,ele_size);
memcpy(arr+(i+1)*ele_size,arr+end*ele_size,ele_size);
memcpy(arr+end*ele_size,tmp,ele_size);
i= (i + 1);
sort(arr,ele_size,start, i - 1,compare);
sort(arr,ele_size,i + 1, end,compare);
}
}
int main()
{
FILE* fp;
int bio_max = 3;
struct Members bio[bio_max]; // Define bio to be large enough.
/* Open FILE and setup bio matrix */
/* For testing */
bio[0].id = 0;
strcpy(bio[0].fname, "");
strcpy(bio[0].lname, "Apple");
bio[0].age = 0;
bio[1].id = 1;
strcpy(bio[1].fname, "");
strcpy(bio[1].lname, "Cat");
bio[1].age = 1;
bio[2].id = 2;
strcpy(bio[2].fname, "");
strcpy(bio[2].lname, "Bat");
bio[2].age = 2;
/* Sort the structure */
sort(bio, sizeof(struct Members), 0, bio_max - 1, struct_cmp);
/* Print the sorted structure */
for (int i = 0; i < bio_max; i++) {
printf("%d %s %s %d\n", bio[i].id, bio[i].fname, \
bio[i].lname, bio[i].age);
}
}
Output
0 Apple 0
2 Bat 2
1 Cat 1
If the strings are not sorting in the way you want, you can redefine the struct_cmp function. Code is self explanatory, the base logic in the code is pass an array and swap elements using memcpy functions. You cant use simple assignment operator if you want to be generic, so that is why the element size is explicitly passed.
Edit
The code was not handling the condition, if lname are same. I missed it thanks for #4386427 for pointing this out.
I think you should define bio to be an array. And google sort algorithms please. Also recommend you google how to use libc function qsort.
I want to modify some (not all) fields of an array (or structs) in multiple threads, with out blocking the rest of the array as the rest of it is being modified in other threads. How is this achieved? I found some answers, but they are for C++ and I want to do it in C.
Here is the code I got so far:
#define _GNU_SOURCE
#include <pthread.h>
#include <stdio.h>
#include <semaphore.h>
#include <stdlib.h>
#include <time.h>
#include <unistd.h>
#define ARRAYLENGTH 5
#define TARGET 10000
int target;
typedef struct zstr{
int* array;
int place;
int run;
pthread_mutex_t* locks;
}zstr;
void *countup(void *);
int main(int argc, char** args){
int al;
if(argc>2){
al=atoi(args[1]);
target=atoi(args[2]);
}else{
al=ARRAYLENGTH;
target=TARGET;
}
printf("%d %d\n", al, target);
zstr* t=malloc(sizeof(zstr));
t->array=calloc(al, sizeof(int));
t->locks=calloc(al, sizeof(pthread_mutex_t));
int* rua=calloc(al, sizeof(int));
pthread_t id[4*al];
for(int i=0; i<al; i++)
pthread_mutex_init(&(t->locks[i]), NULL);
for(int j=0; j<4*al; j++){
int st=j%al;
t->run=rua[st]++;
t->place=st;
pthread_create(&id[j], NULL, &countup, t);
}
for(int k=0; k<4*al; k++){
pthread_join(id[k], NULL);
}
for(int u=0; u<al; u++)
printf("%d\n", t->array[u]);
free(rua);
free(t->locks);
free(t->array);
return 0;
}
void *countup(void* table){
zstr* nu=table;
if(!nu->run){
pthread_mutex_lock(nu->locks + nu->place);
}else{
pthread_mutex_trylock(nu->locks + nu->place);
}
while(nu->array[nu->place]<target)
nu->array[nu->place]++;
pthread_mutex_unlock(nu->locks + nu->place);
return NULL;
}
Sometimes this works just fine, but then calculates wrong values and for quiet sort problems (like the default values), it takes super long (strangely it worked once when I handed them in as parameters).
There isn't anything special about part of an array or structure. What matters is that the mutex or other synchronization you apply to a given value is used correctly.
In this case, it seems like you're not checking your locking function results.
The design of the countup function only allows a single thread to ever access the object, running the value all the way up to target before releasing the lock, but you don't check the trylock result.
So what's probably happening is the first thread gets the lock, and subsequent threads on the same mutex call trylock and fail to get the lock, but the code doesn't check the result. Then you get multiple threads incrementing the same value without synchronization. Given all the pointer dereferences the index and increment operations are not guaranteed to be atomic, leading to problems where the values grow well beyond target.
The moral of the story is to check function results and handle errors.
Sorry, don't have enough reputation to comment, yet.
Adding to Brad's comment of not checking the result of pthread_mutex_trylock, there's a misconception that shows many times with Pthreads:
You assume, that pthread_create will start immediately, and receive the values passed (here pointer t to your struct) and it's content read atomically. That is not true. The thread might start any time later and will find the contents, like t->run and t->place already changed by the next iteration of the j-loop in main.
Moreover, you might want to read David Butenhof's book "Programming with Posix Threads" (old, but still a good reference) and check on synchronization and condition variables.
It's not that good style to start that many threads in the first place ;)
As this has come up a few times and might come up again, I have restructured that a bit to issue work_items to the started threads. The code below might be amended by a function, that maps the index into array to always the same area_lock, or by adding a queue to feed the running threads with further work-item...
#include <assert.h>
#include <stdio.h>
#include <stdlib.h>
#include <time.h>
#include <unistd.h>
#include <pthread.h>
/*
* Macros for default values. To make it more interesting, set:
* ARRAYLENGTH != THREADS
* INCREMENTS != TARGET
* NUM_AREAS != THREADS
* Please note, that NUM_AREAS must be <= ARRAY_LENGTH.
*/
#define ARRAYLENGTH 10
#define TARGET 100
#define INCREMENTS 10
#define NUM_AREAS 2
#define THREADS 5
/* These variables are initialized once in main, then only read... */
int array_len;
int target;
int num_areas;
int threads;
int increments;
/**
* A long array that is going to be equally split into number of areas.
* Each area is covered by a lock. The number of areas do not have to
* equal the length of the array, but must be smaller...
*/
typedef struct shared_array {
int * array;
int num_areas;
pthread_mutex_t * area_locks;
} shared_array;
/**
* A work-item a thread is assigned to upon startup (or later on).
* Then a value of { 0, any } might signal the ending of this thread.
* The thread is working on index within zstr->array, counting up increments
* (or up until the target is reached).
*/
typedef struct work_item {
shared_array * zstr;
int work_on_index;
int increments;
} work_item;
/* Local function declarations */
void * countup(void *);
int main(int argc, char * argv[]) {
int i;
shared_array * zstr;
if (argc == 1) {
array_len = ARRAYLENGTH;
target = TARGET;
num_areas = NUM_AREAS;
threads = THREADS;
increments = INCREMENTS;
} else if (argc == 6) {
array_len = atoi(argv[1]);
target = atoi(argv[2]);
num_areas = atoi(argv[3]);
threads = atoi(argv[4]);
increments = atoi(argv[5]);
} else {
fprintf(stderr, "USAGE: %s len target areas threads increments", argv[0]);
exit(-1);
}
assert(array_len >= num_areas);
zstr = malloc(sizeof (shared_array));
zstr->array = calloc(array_len, sizeof (int));
zstr->num_areas = num_areas;
zstr->area_locks = calloc(num_areas, sizeof (pthread_mutex_t));
for (i = 0; i < num_areas; i++)
pthread_mutex_init(&(zstr->area_locks[i]), NULL);
pthread_t * id = calloc(threads, sizeof (pthread_t));
work_item * work_items = calloc(threads, sizeof (work_item));
for (i = 0; i < threads; i++) {
work_items[i].zstr = zstr;
work_items[i].work_on_index = i % array_len;
work_items[i].increments = increments;
pthread_create(&(id[i]), NULL, &countup, &(work_items[i]));
}
// Let's just do this one work-item.
for (i = 0; i < threads; i++) {
pthread_join(id[i], NULL);
}
printf("Array: ");
for (i = 0; i < array_len; i++)
printf("%d ", zstr->array[i]);
printf("\n");
free(id);
free(work_items);
free(zstr->area_locks);
free(zstr->array);
return 0;
}
void *countup(void* first_work_item) {
work_item * wi = first_work_item;
int inc;
// Extract the information from this work-item.
int idx = wi->work_on_index;
int area = idx % wi->zstr->num_areas;
pthread_mutex_t * lock = &(wi->zstr->area_locks[area]);
pthread_mutex_lock(lock);
for (inc = wi->increments; inc > 0 && wi->zstr->array[idx] < target; inc--)
wi->zstr->array[idx]++;
pthread_mutex_unlock(lock);
return NULL;
}
I'm trying to solve a small problem of synchronization. but when I join the threads i get segment fault of the fifth iteration! If i only create 4 threads works perfect.
Here I leave the code with some basics of what to do the thread.
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <pthread.h>
#include <semaphore.h>
sem_t HackersEmploy_Counter;
int hackerOnBoat, employOnBoat, B, b, hackerResagado, employResagado;
sem_t Board;
int onBoatId[4];
char onBoatType[4];
sem_t Bote;
typedef struct{
FILE* log;
int ID;
}param;
void* HackerArrive(void* para){
param* var = (param*) para;
printf("Create Hacker %i\n", var->ID-1);
pthread_exit(0);
}
void* EmployeeArrive(void* para){
param* var = (param*) para;
printf("Create Employee %i\n", var->ID-1);
pthread_exit(0);
}
int main(int argc, char **argv) {
sem_init(&HackersEmploy_Counter,0,1);
sem_init(&Bote,0,4);
sem_init(&Board,0,1);
FILE* log;
log = fopen("result_simulacion.txt", "w");
int E, e=1, H, h=1, i, r;
hackerOnBoat=0; employOnBoat=0; b=1; hackerResagado=0; employResagado=0;
for (i=1; i<argc; i++){
if (strcmp(argv[i],"-h")==0){
i++;
H = atoi(argv[i]);
}
if (strcmp(argv[i],"-e")==0){
i++;
E = atoi(argv[i]);
}
if (strcmp(argv[i],"-b")==0){
i++;
B = atoi(argv[i]);
}
}
pthread_t* bank = (pthread_t*) malloc( (E+H) * sizeof (pthread_t*));
param* var = (param*) malloc( (E+H) + sizeof (param*));
for (i=0; i<H+E; i++){
r = rand() % 2;
if (r==0){
if (h<=H){
var[i].log = log;
var[i].ID = h;
pthread_create(&bank[i], NULL, HackerArrive, (void*) &var[i]);
h++;
}else{
var[i].log = log;
var[i].ID = e;
pthread_create(&bank[i], NULL, EmployeeArrive, (void*) &var[i]);
e++;
}
}else{
if (e<=E){
var[i].log = log;
var[i].ID = e;
pthread_create(&bank[i], NULL, EmployeeArrive, (void*) &var[i]);
e++;
}else{
var[i].log = log;
var[i].ID = h;
pthread_create(&bank[i], NULL, HackerArrive, (void*) &var[i]);
h++;
}
}
}
for (i=0; i<E+H; i++){
pthread_join(bank[i], NULL);
printf("join %i\n", i);
}
return 0;
}
run with: ./work -h 4 -e 0 -b 1
them run with: ./work -h 5 -e 0 -b 1
if they increase the value of "-h" more than 4 receive segmentation fault
Why this?
The allocation for your array of pthread_ts isn't quite right. You want to allocate storage for an array of pthread_t instances but are currently only allocating space for pointers.
By not allocating space for the correct type, you risk writing beyond the end of your allocated memory when you later write to this array. This has undefined consequences; in this case you're likely to over-write memory used by other parts of your program. This may well cause a segfault.
You should change the allocation as follows
pthread_t* bank = malloc((E+H) * sizeof(*bank));
The same issue applies to the param array var which should be allocated like
param* var = malloc((E+H) + sizeof(*var));
Note that it'd be good practice for you to free these arrays later in your program
for (i=0; i<E+H; i++){
pthread_join(bank[i], NULL);
printf("join %i\n", i);
}
free(bank);
free(var);
Please be more careful reading your code. This:
param* var = malloc((E+H) + sizeof(*var));
should be this:
param* var = malloc((E+H) * sizeof(*var));
// ^
Your code works (or at least, doesn't crash) for me after this change:
paul#local:~/src/c/scratch$ ./thread -h 2 -e 2 -b 2
Create Employee 0
Create Hacker 1
Create Employee 1
Create Hacker 0
join 0
join 1
join 2
join 3
paul#local:~/src/c/scratch$
Using a tool like valgrind would have helped you track this one down in minutes.
As mentioned in my other comment, you should check the return every single time you call functions like malloc(), fopen(), pthread_create(), pthread_join(), and so on, otherwise you have absolutely no idea whether you're ignoring important errors your functions are trying to tell you about.
I'm trying to do a simple multi-threaded consumer/producer, where multiple reader and writer thread, read from a file to the buffer and then from buffer back into a file. It should be thread safe. however, it is not performing as i expected. It halts half way but everytime on a different line?
Please help me understand what I am doing wrong?!?
#include <pthread.h>
#include <stdio.h>
#include <stdlib.h>
//TODO Define global data structures to be used
#define BUF_SIZE 5
FILE *fr;
FILE *to; /* declare the file pointer */
struct _data {
pthread_mutex_t mutex;
pthread_cond_t cond_read;
pthread_cond_t cond_write;
int condition;
char buffer[BUF_SIZE];
int datainbuffer;
}dc1 = {
PTHREAD_MUTEX_INITIALIZER,PTHREAD_COND_INITIALIZER,PTHREAD_COND_INITIALIZER,0,{0},0
};
void *reader_thread(void *arg) {
//TODO: Define set-up required
struct _data *d = (struct _data *)arg;
int killreaders = 0;
while(1) {
//TODO: Define data extraction (queue) and processing
pthread_mutex_lock(&d->mutex);
while (d->condition == 0 || d->datainbuffer<=0){
pthread_cond_wait( &d->cond_read, &d->mutex );
if(killreaders == 1){
pthread_mutex_unlock(&d->mutex);
pthread_cond_signal(&d->cond_read);
pthread_cond_signal(&d->cond_write);
return NULL;
}
}
d->condition = 0;
int i;
char res;
//if the buffer is not full, that means the end of file is reached and it time to kill the threads remaining.
if(d->datainbuffer!=BUF_SIZE)
killreaders = 1;
for (i=0; i<(sizeof d->datainbuffer); i++) {
res = d->buffer[i];
printf("to file:%c",res);
fputc(res, to);
}
d->datainbuffer = 0;
pthread_mutex_unlock(&d->mutex);
pthread_cond_signal( &d->cond_write );
}
return NULL;
}
void *writer_thread(void *arg) {
//TODO: Define set-up required
struct _data *d = (struct _data *)arg;
char * pChar;
int killwriters = 0;
while(1){
pthread_mutex_lock(&d->mutex);
while( d->condition == 1 || d->datainbuffer>0){
pthread_cond_wait( &d->cond_write, &d->mutex );
if(killwriters==1){
pthread_mutex_unlock(&d->mutex);
pthread_cond_signal(&d->cond_write);
pthread_cond_signal(&d->cond_read);
return NULL;
}
}
d->condition = 1;
int i;
char rc;
for (i = 0; i < BUF_SIZE; i++){
if((rc = getc(fr)) == EOF){
killwriters = 1;
pthread_mutex_unlock(&d->mutex);
pthread_cond_signal(&d->cond_read);
return NULL;
}
d->datainbuffer = i+1;
d->buffer[i] = rc;
printf("%c",rc);
}
int m = 0;
pthread_mutex_unlock(&d->mutex);
pthread_cond_signal(&d->cond_read);
}
return NULL;
}
#define M 10
#define N 20
int main(int argc, char **argv) {
struct _data dc=dc1;
fr = fopen ("from.txt", "rt"); /* open the file for reading */
if (fr == NULL)
{
printf("Could not open file!");
return 1;
}
to = fopen("to.txt", "wt");
int i;
pthread_t readers[N];
pthread_t writers[M];
for(i = 0; i < N; i++) {
pthread_create(&readers[i], NULL, reader_thread, (void*)&dc);
}
for(i = 0; i < M; i++) {
pthread_create(&writers[i], NULL, writer_thread, (void*)&dc);
}
fclose(fr);
fclose(to);
return 0;
}
any suggestion is appreciated!
Your threads are reading from and writing to files, which you open & close in main. But main doesn't explicitly wait for the threads to finish before closing those files.
In addition to the problem pointed out by Scott Hunter, your readers and writers do all their "real work" while holding the mutex, defeating the point of having more than one thread in the first place.
Readers should operate as follows:
1) Acquire mutex.
2) Block on the condition variable until work is available.
3) Remove work from queue, possibly signal condition variable.
4) Release mutex.
5) Process the work.
6) Go to step 1.
Writers should operate as follows:
1) Get the information we need to write.
2) Acquire the mutex.
3) Block on the condition variable until there is space on the queue.
4) Place information in the queue, possibly signal condition variable.
5) Release the mutex.
6) Go to step 1.
Notice both threads do the "real work" without holding the mutex? Otherwise, why have multiple threads if only one of them can do work at a time?
I'm not sure whether my answer is going to help you or not.. but I'm going to give my best by giving you some reference code.
I have written a similar program (except that it does not write to the file, instead display the queue-/produced-/consumed- items in the stdout). It can be found here - https://github.com/sangeeths/pc . I have separated the command-line processing and queue logic into a separate files.
Hope this helps!