45779

Using Both Conditional Variables & Mutex to Synchronize Threads in C

Question:

Edited as per commenter's request.

This program creates two threads. Each thread reads from one of two specific input files, each of which contains either one letter or one '0' per line of code. The threads are supposed to read the letters into a global char array, which is then printed. The problem is that, upon reaching a '0,' the active thread must transfer control to the other thread, which should not have a '0' on that line. (We are sure that, if File 1 has a '0' on a line, then File 2, on the corresponding line, has a letter. Multiple zeros can follow one another, as can multiple letters.)

FILE ONE

h 0 h 0 h 0 h 0 h 0

FILE TWO

0 i 0 i 0 i 0 i 0 i

I am attempting to use pthread mutex lock/unlock as well as signal and wait to make this work. However, I keep reaching a state of deadlock.

There are <strong>two</strong> threads. Currently, they mirror each other meaning that they do the same things, just with different files and opposite conditions.

Thread Example:

char final[1001]; pthread_mutex_t lock = PTHREAD_MUTEX_INITIALIZER; pthread_cond_t condition1 = PTHREAD_COND_INITIALIZER; pthread_cond_t condition2 = PTHREAD_COND_INITIALIZER; int w = 1; void *get() { //start reading while (count < //number) { pthread_mutex_lock(&lock); //read line //if we've reached a zero { w = 2; while(w == 2) { pthread_cond_wait(&condition1, &lock); } pthread_mutex_unlock(&lock); } else { if(w == 1) { if(strlen(placeHolderChars)>0) { placeHolderChars[1] = '\0'; } //copy char to array w= 2; pthread_cond_signal(&condition2); pthread_mutex_unlock(&lock); } } if(feof(file)) { fclose(file); break; } count++; } return 0; }

UPDATE: Signal-before-wait strategy did not really work when using a larger file. Still working on this!

Answer1:

This code seems to work. The primary significant change is to add pthread_cond_signal() on the the other thread's condition before going into the while (who == N) loops.

Other changes include basic debug printing to make it easier to see what's going on an which thread is doing what. Note that debugging messages end with newlines.

#include <assert.h> #include <pthread.h> #include <stdio.h> #include <stdlib.h> #include <string.h> extern void *getMessage1(void *arg); extern void *getMessage2(void *arg); static char message[4096]; int main(void) { pthread_t id1; pthread_t id2; pthread_create((&id1), NULL, getMessage1, NULL); pthread_create((&id2), NULL, getMessage2, NULL); pthread_join(id1, NULL); pthread_join(id2, NULL); for (int j = 0; j < 1001 && message[j] != '\0'; j++) printf("%c ", message[j]); putchar('\n'); return 0; } static pthread_mutex_t lock = PTHREAD_MUTEX_INITIALIZER; static pthread_cond_t condition1 = PTHREAD_COND_INITIALIZER; static pthread_cond_t condition2 = PTHREAD_COND_INITIALIZER; static int who = 1; void *getMessage1(void *arg) { assert(arg == NULL); const char filename[] = "Student1"; FILE *studentOne = fopen(filename, "r"); if (studentOne == NULL) { fprintf(stderr, "Failed to open file %s for reading\n", filename); exit(EXIT_FAILURE); } size_t howManyChars; char *placeHolderChars; int count = 1; while (count < 501) { placeHolderChars = NULL; if (getline(&placeHolderChars, &howManyChars, studentOne) == -1) break; printf("M1(%d): [%s]\n", count, placeHolderChars); pthread_mutex_lock(&lock); if (strcmp(placeHolderChars, "0\n") == 0) { printf("M1: Two's turn - 1\n"); pthread_cond_signal(&condition2); who = 2; while (who == 2) { pthread_cond_wait(&condition1, &lock); } free(placeHolderChars); } else { if (who == 1) { if (strlen(placeHolderChars) > 0) { placeHolderChars[1] = '\0'; } strcat(message, placeHolderChars); free(placeHolderChars); who = 2; pthread_cond_signal(&condition2); } else printf("M1: Two's turn - 2\n"); } pthread_mutex_unlock(&lock); count++; } fclose(studentOne); return 0; } void *getMessage2(void *arg) { assert(arg == NULL); const char filename[] = "Student2"; FILE *studentTwo = fopen(filename, "r"); if (studentTwo == NULL) { fprintf(stderr, "Failed to open file %s for reading\n", filename); exit(EXIT_FAILURE); } size_t howManyChars; char *placeHolderChars; int count = 0; while (count < 501) { placeHolderChars = NULL; if (getline(&placeHolderChars, &howManyChars, studentTwo) == -1) break; printf("M2(%d): [%s]\n", count, placeHolderChars); pthread_mutex_lock(&lock); if (strcmp(placeHolderChars, "0\n") == 0) { printf("M2: One's turn - 1\n"); pthread_cond_signal(&condition1); who = 1; while (who == 1) { pthread_cond_wait(&condition2, &lock); } free(placeHolderChars); } else { if (who == 2) { if (strlen(placeHolderChars) > 0) { placeHolderChars[1] = '\0'; } strcat(message, placeHolderChars); free(placeHolderChars); who = 1; pthread_cond_signal(&condition1); } else printf("M2: One's turn - 2\n"); } pthread_mutex_unlock(&lock); count++; } fclose(studentTwo); return 0; }

You should be able to refine the code such that you pass a structure containing the relevant per-thread data (file name, current thread condition, other thread condition, maybe a 'thread ID') to a single function, so you have just getMessage().

Output:

M1(1): [h ] M2(0): [0 ] M1(2): [0 ] M2: One's turn - 1 M1: Two's turn - 1 M2(1): [i ] M2(2): [0 ] M2: One's turn - 1 M1(3): [h ] M1(4): [0 ] M1: Two's turn - 1 M2(3): [i ] M2(4): [0 ] M2: One's turn - 1 M1(5): [h ] M1(6): [0 ] M1: Two's turn - 1 M2(5): [i ] M2(6): [0 ] M2: One's turn - 1 M1(7): [h ] M1(8): [0 ] M1: Two's turn - 1 M2(7): [i ] M2(8): [0 ] M2: One's turn - 1 M1(9): [h ] M1(10): [0 ] M1: Two's turn - 1 M2(9): [i ] h i h i h i h i h i <hr />

I'm not completely happy with this code. I created a modified version with a single function used by both threads, as I hinted should be done, and modified the printing of the lines read to avoid printing the newlines (making the output more compact). Sometimes — not all the time — it would deadlock at the end. Two sample traces, one working, one deadlocking (program name pth47):

$ pth47 M2(1): [0] M2: 1's turn - 1 M1(1): [h] M1(2): [0] M1: 2's turn - 1 M2(2): [i] M2(3): [0] M2: 1's turn - 1 M1(3): [h] M1(4): [0] M1: 2's turn - 1 M2(4): [i] M2(5): [0] M2: 1's turn - 1 M1(5): [h] M1(6): [0] M1: 2's turn - 1 M2(6): [i] M2(7): [0] M2: 1's turn - 1 M1(7): [h] M1(8): [0] M1: 2's turn - 1 M2(8): [i] M2(9): [0] M2: 1's turn - 1 M1(9): [h] M1(10): [0] M1: 2's turn - 1 M2(10): [i] h i h i h i h i h i $ pth47 M1(1): [h] M2(1): [0] M1(2): [0] M2: 1's turn - 1 M1: 2's turn - 1 M2(2): [i] M2(3): [0] M2: 1's turn - 1 M1(3): [h] M1(4): [0] M1: 2's turn - 1 M2(4): [i] M2(5): [0] M2: 1's turn - 1 M1(5): [h] M1(6): [0] M1: 2's turn - 1 M2(6): [i] M1(7): [h] M1(8): [0] M2(7): [0] M1: 2's turn - 1 M2: 1's turn - 1 M1(9): [h] M1(10): [0] M2(8): [i] M1: 2's turn - 1 M2(9): [0] M2: 1's turn - 1 ^C $

I've not tracked down the aberration. It isn't as simple as 'thread one went first'; there are examples where thread one went first and it completed fine.

Answer2:

I think the person who posed the question did it twice, a bit annoying. FWIW, here is my answer to the duplicate: <a href="https://stackoverflow.com/questions/46384349/pthread-synchronization-back-forth-reading-of-two-text-files/46406591#46406591" rel="nofollow">Pthread Synchronization: Back & Forth Reading of Two Text Files</a>

Recommend

  • What happens if no threads are waiting and condition signal was sent?
  • Pthread_t not starting
  • Thread Synchronization C++
  • threads have the same id
  • A program to find length of string
  • afplay Error: AudioFileOpen failed (-43)
  • How to call RESTful WCF-Service from PHP
  • SQLite not storing blobs containing \\0 bytes
  • Annoying tick with speex [closed]
  • Frama-C anagram function behavior verification
  • How to Send a structure using sendto()
  • Reading a line from file in C, dynamically
  • How can I exit a while(1) based on a user input?
  • How to check if one div overlapped another during transition from one position to another
  • When is InputStream.available() useful?
  • Dropping support for JRE 1.3
  • Domain name setup with AWS S3 bucket with static hosting
  • PHP socket_read() only gets the first byte of the stream
  • Strings appear exact, but they do not match?
  • How can I prevent the need to copy strings passed to a avr-gcc C++ constructor?
  • allocating memory to an array of string
  • SAVE attribute needed for Fortran variables when only the C_LOC address is returned to a C program?
  • Using Sax parsing to edit and write XML in VB6
  • Django: Count of Group Elements
  • Is there a javascript serializer for JSON.Net?
  • Sending data from AppleScript to FileMaker records
  • MySQL WHERE-condition in procedure ignored
  • Where to put my custom functions in Wordpress?
  • Rearranging Cells in UITableView Bug & Saving Changes
  • Circular dependency while pushing http interceptor
  • Linker errors when using intrinsic function via function pointer
  • Buffer size for converting unsigned long to string
  • FormattedException instead of throw new Exception(string.Format(…)) in .NET
  • costura.fody for a dll that references another dll
  • Binding checkboxes to object values in AngularJs
  • Observable and ngFor in Angular 2
  • How to Embed XSL into XML
  • UserPrincipal.Current returns apppool on IIS
  • Conditional In-Line CSS for IE and Others?
  • java string with new operator and a literal