简体   繁体   中英

Synchronizing pthreads using mutex in C

I've got to write a program that counts series of first 10 terms (sorry for my language, this is the first time that I'm talking about math in english) given by formula (x^i)/i!. So, basically it's trivial. BUT, there's some special requirements. Every single term got to be counted by seperated thread, each of them working concurrent. Then all of them got to save results to common variable named result . After that they have to be added by main thread, which will display final result. All of it using pthreads and mutexes.

That's where I have a problem. I was thinking about using table to store results, but I was told by teacher, that it's not correct solution, cause then I don't have to use mutexes. Any ideas what to do and how to synchronize it? I'm completely new to pthread and mutex.

Here's what I got till now. I'm still working on it, so it's not working at the moment, it's just a scheme of a program, where I want to add mutexes. I hope it's not all wrong. ;p

#include <stdio.h>
#include <stdlib.h>  
#include <math.h>
#include <pthread.h>

int number = 0;
float result = 0;
pthread_mutex_t term_lock;
pthread_mutex_t main_lock;
int save = 0; //condition variable

int factorial(int x) { 
        if(x==0 || x==1)
                return 1;

        return factorial(x-1)*x;
}

void  *term(void *value) {  
        int x = *(int *)value;
        float w;
        if(save == 0) {
            pthread_mutex_lock(&term_lock);
            w = pow(x, number)/factorial(number);
            result = w;
            printf("%d term of series with x: %d  is: %f\n", number, x, w);
            number++;
            save = 1;
            pthread_mutex_unlock(&term_lock);
        }
        return NULL;
}

int main(void) {

        int x, i, err = 0;
        float final = 0;
        pthread_t threads[10];

        pthread_attr_t attr;
        pthread_attr_init(&attr);
        pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED);

        printf("Get X: \n");
        scanf("%d", &x);
        for(i=0; i<10; i++)
        {
                err = pthread_create(&threads[i], &attr, (void *)term, &x);
                if(err) {
                        printf("Error creating threads.\n");
                        exit(-1);
                }
        }
        i = 0;
        while (number <= 10) {
            //printf("While Result: %f, final %f\n", result, final); - shows that it's infinite loop
            if(save) {
                pthread_mutex_lock(&main_lock); 
                final = final + result;
                save = 0;
                pthread_mutex_unlock(&main_lock);   
                printf("If Result: %f, final %f\n", result, final); //final == last result
            }
        }
        return 0;
}

EDIT: If it's not clear - I need help with solution how to store results of all threads in common variable and synchronizing it.

EDIT2: Possible solution - global variable result shared by all threads. Returned to main thread, it would be added to some local variable, so then I could just overwrite it's value with result from another thread. Of course it will require some synchronization, so another thread won't overwrite it before I add it in main thread. What do you think?

EDIT3: I've updated code with what I have right now. Output is giving me me values of 8-9 terms (printf in term), then program is still working, showing nothing. Commented printf showed me, that while loop is infinite. Also local variable final has just last value of result. What am I doing wrong?

It's rather contrived that the main thread should be the one to add the terms, but the individual threads must all write their results to the same variable. I would ordinarily expect each thread to add its own term to the result (which does require mutex), or possibly to put its result in an array (as you suggested), or to add it to a shared queue (which would require mutex), or even to write it to a pipe. Nevertheless, it can be done your teacher's way.

One of the key problems to solve is that you have to distinctly different operations that you need to synchronize:

  • The various computational threads' writes to the shared result variable
  • The main thread's reads of the result variable

You cannot use just a single synchronization construct because you cannot that way distinguish between the computational threads and the main thread. One way to approach this would be to synchronize the computational threads' writes via a mutex, as required, and to synchronize those vs . the main thread's reads via semaphores or condition variables. You could also do it with one or more additional mutexes, but not cleanly.

Additional notes:

  • the result variable in which your threads deposit their terms must be a global. Threads do not have access to the local variables of the function from which they are launched.
  • the signature of your term() function is incorrect for a thread start function. The argument must be of type void * .
  • thread start functions are no different from other functions in that their local variables are accessible only for the duration of the function execution. In particular, returning a pointer to a local variable cannot do anything useful, as any attempt to later dereference such a pointer produces undefined behavior.

I'm not going to write your homework for you, but here's an approach that can work:

  1. The main thread initializes a mutex and two semaphores, the latter with initial values zero.
  2. The main thread launches all the computational threads. Although it's ugly, you can feed them their numeric arguments by casting those to void * , and then casting them back in the term() function (since its argument should be a void * ).
  3. The main thread then loops. At each iteration, it
    1. waits for semaphore 1 ( sem_wait() )
    2. adds the value of the global result variable to a running total
    3. posts to semaphore 2 ( sem_post() )
    4. if as many iterations have been performed as there are threads, breaks from the loop

Meanwhile, each computational thread does this:

  1. Computes the value of the appropriate term
  2. locks the mutex
  3. stores the term value in the global result variable
  4. posts to semaphore 1
  5. waits for semaphore 2
  6. unlocks the mutex

Update:

To use condition variables for this job, it is essential to identify which shared state is being protected by those condition variables, as one must always protect against waking spurriously from a wait on a condition variable.

In this case, it seems natural that the shared state in question would involve the global result variable in which the computational threads return their results. There are really two general, mutually exclusive states of that variable:

  1. Ready to receive a value from a computational thread, and
  2. Ready for the main thread to read.

The computational threads need to wait for the first state, and the main thread needs to wait (repeatedly) for the second. Since there are two different conditions that threads will need to wait on, you need two condition variables. Here's an alternative approach using these ideas:

  1. The main thread initializes a mutex and two condition variables, and sets result to -1 .
  2. The main thread launches all the computational threads. Although it's ugly, you can feed them their numeric arguments by casting those to void * , and then casting them back in the term() function (since its argument should be a void * ).
  3. The main thread locks the mutex
  4. The main thread then loops. At each iteration, it
    1. tests whether result is non-negative. If so, it
      1. adds the value of result variable to a running total
      2. if as many terms have been added as there are threads, breaks from the loop
      3. sets result to -1 .
      4. signals condition variable 1
    2. waits on condition variable 2
  5. Having broken from the loop, the main thread unlocks the mutex

Meanwhile, each computational thread does this:

  1. Computes its term
  2. Locks the mutex
  3. Loops:
    1. Checks the value of result . If it is less than zero then breaks from the loop
    2. waits on condition variable 1
  4. Having broken from the loop, sets result to the computed term
  5. signals condition variable 2
  6. unlocks the mutex

The number is shared between all the threads, so you will need to protect that with a mutex (which is probably what your teacher is wanting to see)

pthread_mutex_t number_mutex;
pthread_mutex_t result_mutex;
int number = 0;
int result = 0;

void  *term(int x) {
  float w;

  // Critical zone, make sure only one thread updates `number`
  pthread_mutex_lock(&number_mutex); 
  int mynumber = number++;
  pthread_mutex_unlock(&number_mutex);
  // end of critical zone 

  w = pow(x, mynumber)/factorial(mynumber);
  printf("%d term of series with x: %d  is: %f\n", mynumber, x, w);

  // Critical zone, make sure only one thread updates `result`
  pthread_mutex_lock(&result_mutex); 
  result += w;
  pthread_mutex_unlock(&result_mutex);
  // end of critical zone 

  return (void *)0;
}

You should also remove the DETACHED state and do a thread-join at the end of your main program before printing out the result

Here is my solution to your problem:

#include <stdio.h>
#include <stdlib.h>
#include <math.h>
#include <pthread.h>

int number=0;
float result[10];
pthread_mutex_t lock;

int factorial(int x) {


    if(x==0 || x==1)
                return 1;

        return factorial(x-1)*x;
}

void  *term(void *value) {
        int x = *(int *)value;
        float w;
        pthread_mutex_lock(&lock);
        w = pow(x, number)/factorial(number);
        printf("%d term of series with x: %d  is: %f\n", number, x, w);
        result[number] = w;
        number++;
        pthread_mutex_unlock(&lock);
        return NULL;
}

int main(void) {

        int x, i, err;
        pthread_t threads[10];

        printf("Get X: \n"); 
        scanf("%d", &x);

        for(i=0; i<=9; i++)
        {
                err = pthread_create(&threads[i], NULL, term, &x);
                if(err) {
                        printf("Error creating threads.\n");
                        exit(-1);
                }
        }

        for(i = 0; i < 10; i++)
        {
                pthread_join(threads[i], NULL);
        }

        i = 0;

        for(i=0; i<=9; i++)
        {
                printf("%f\n", result[i]);
        }
        return 0;
}

This code creates a global mutex pthread_mutex_t lock that (in this case) makes sure that same code is not executed by anyone at the same time: basically when one thread executes pthread_mutex_lock(&lock) , it forbids any other thread from executing that part of the code until the "original" thread executes pthread_mutex_unlock(&lock) .

The other important part is pthread_join : what this does is force the main thread to wait for the execution of every other thread created; this way, float result[10] is written before actually being worked on in the main thread (in this case, the last print instruction).

Other than that, I fixed a couple of bugs in your code that other users pointed out.

If result is to be a single variable, then one solution is to use an array of 20 mutexes: aMutex[20];. Main locks all 20 mutexes then starts the pthreads. Each pthread[i] computes a local term, waits for aMutex[i], stores it's value into result, then unlocks aMutex[10+i]. In main() for(i = 0; i < 20; i++){ unlock aMutex[i] to allow pthread[i] to store its value into result, then wait for aMutex[10+i] to know that result is updated, then add result to a sum. }

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM