Multithreaded (c) program threads cannot be terminated

I'm trying to complete a program that uses multiple threads (3) to distribute $4000 hypothetical scholarships Every time a thread processes, it "locks" the critical area and prevents other threads from taking their blocks from the sum Each time you visit, this post will occupy 25% of the "scholarship" balance The output is the amount required for each thread to obtain scholarship access

So far, my program seems to be processing the correct output, but there seems to be a problem when it reaches the end Each process / thread reaches a program that does not terminate or exit, and the program will stagnate and cannot be completed I think the thread is processing, but the termination condition is not met (all scholarships disappear) The last function totalcalc() will never be reached Does anyone see what I don't have, which helps alleviate the problem or promote the completion of the program?

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

#define PERCENTAGE 0.25

pthread_mutex_t mutex; // protecting critical section
int scholarship = 4000,total = 0;
void *A();
void *B();
void *C();
void *totalCalc();

int main(){ 

    pthread_t tid1,tid2,tid3;

    //pthread_setconcurrency(3); 

    pthread_create(&tid1,NULL,(void *(*)(void *))A,NULL );
    pthread_create(&tid2,(void *(*)(void *))B,NULL );
    pthread_create(&tid3,(void *(*)(void *))C,NULL );
    pthread_join(tid1,NULL);
    pthread_join(tid2,NULL);
    pthread_join(tid3,NULL);

    totalCalc();


    return 0;

}

void *A(){
    float result;
    while(scholarship > 0){
        sleep(2);
        pthread_mutex_lock(&mutex);
        result = scholarship * PERCENTAGE;
        result = ceil(result);
        total = total + result;
        scholarship = scholarship - result;
        if( result >= 1){
            printf("A = ");
            printf("%.2f",result);
            printf("\n");
        }
        if( scholarship < 1){
            pthread_exit(0);
            printf("Thread A exited\n");
            return;
        }
        pthread_mutex_unlock(&mutex);
    }

    pthread_exit(0);

}

void *B(){
    float result;
    while(scholarship > 0){
        sleep(1);
        pthread_mutex_lock(&mutex);
        result = scholarship * PERCENTAGE;
        result = ceil(result);
        total = total + result;
        scholarship = scholarship - result;
        if( result >= 1){
            printf("B = ");
            printf("%.2f",result);
            printf("\n");
        }
        if( scholarship < 1){
            pthread_exit(0);
            printf("Thread B exited\n");
            return;
        }           
        pthread_mutex_unlock(&mutex);
    }

    pthread_exit(0);
}

void *C(){
    float result;
    while(scholarship > 0){
        sleep(1);
        pthread_mutex_lock(&mutex);
        result = scholarship * PERCENTAGE;
        result = ceil(result);
        total = total + result;
        scholarship = scholarship - result;
        if( result >= 1){
            printf("C = ");
            printf("%.2f",result);
            printf("\n");
        }
        if( scholarship < 1){
            pthread_exit(0);
            printf("Thread C exited\n");
            return;
        }           
        pthread_mutex_unlock(&mutex);       
    }

    pthread_exit(0);
}

void *totalCalc(){
    printf("Total given out: ");
    printf("%d",total);
    printf("\n");
}

Output:

B = 1000.00
C = 750.00
A = 563.00
B = 422.00
C = 317.00
B = 237.00
C = 178.00
A = 134.00
B = 100.00
C = 75.00
B = 56.00
C = 42.00
A = 32.00
B = 24.00
C = 18.00
B = 13.00
C = 10.00
A = 8.00
B = 6.00
C = 4.00
B = 3.00
C = 2.00
A = 2.00
B = 1.00
C = 1.00
B = 1.00
C = 1.00
^C

Solution

You shouldn't write out the same function three times - you can pass a parameter to a thread function to do different things for it

>You should initialize your mutex. > You should use one printf () statement instead of three consecutive. > You should unlock the mutex before exiting the thread function. > You should print the status before exiting the function. > When writing a return, a value should be returned from the thread function. > After folding the totalcalc() function into a single printf() call, it does not have many functions. > Improper use of the word "percentage"; It is a score, not a percentage

I chose to use return instead of calling pthread_ exit(); Differences are not important

Group I cleaning

This is a revision of your code

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

#define FRACTION 0.25

static pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;
static int scholarship = 4000;
static int total = 0;
static void *calculate(void *data);

struct Data
{
    const char *name;
    int doze;
};

int main(void)
{
    pthread_t tid1;
    pthread_t tid2;
    pthread_t tid3;
    struct Data a = { "A",2 };
    struct Data b = { "B",1 };
    struct Data c = { "C",1 };

    pthread_create(&tid1,calculate,&a);
    pthread_create(&tid2,&b);
    pthread_create(&tid3,&c);
    pthread_join(tid1,NULL);

    printf("Total given out: %d\n",total);

    return 0;
}

static void *calculate(void *arg)
{
    struct Data *data = arg;
    float result;
    while (scholarship > 0)
    {
        sleep(data->doze);
        pthread_mutex_lock(&mutex);
        result = scholarship * FRACTION;
        result = ceil(result);
        total = total + result;
        scholarship = scholarship - result;
        if (result >= 1)
        {
            printf("%s = %.2f\n",data->name,result);
        }
        if (scholarship < 1)
        {
            printf("Thread %s exited\n",data->name);
            pthread_mutex_unlock(&mutex);
            return 0;
        }
        pthread_mutex_unlock(&mutex);
    }

    return 0;
}

And sample output (running MacOS Sierra 10.12.6 on MAC, using gcc 7.1.0):

B = 1000.00
C = 750.00
A = 563.00
B = 422.00
C = 317.00
B = 237.00
C = 178.00
A = 134.00
B = 100.00
C = 75.00
B = 56.00
C = 42.00
A = 32.00
C = 24.00
B = 18.00
C = 13.00
B = 10.00
A = 8.00
C = 6.00
B = 4.00
B = 3.00
C = 2.00
A = 2.00
B = 1.00
C = 1.00
B = 1.00
C = 1.00
Thread C exited
Thread A exited
Thread B exited
Total given out: 4000

Phase I improvement

Remember: working code can usually be improved This is another revision of the calculate () function, which can handle termination conditions more clearly

static void *calculate(void *arg)
{
    struct Data *data = arg;
    while (scholarship > 0)
    {
        sleep(data->doze);
        pthread_mutex_lock(&mutex);
        float result = ceil(scholarship * FRACTION);
        total += result;
        scholarship -= result;
        if (result >= 1)
            printf("%s = %.2f\n",result);
        pthread_mutex_unlock(&mutex);
    }
    printf("Thread %s exited\n",data->name);

    return 0;
}

It still uses mixed mode algorithms (floating point and integer) Further improvements will involve modifying the main () function to use arrays instead of separate variables of thread ID and control structure Then you can easily have 2 - 26 threads You can also use sub - second sleep You may have different threads that are different from the rest of the grant - instead of a fixed part, you can use different scores in different threads

Full song, full dance version

There was a problem in previous versions (as user 3629249 pointed out in comment - although I already have a preliminary version of the code with the necessary fixes; it's not yet on so) The code in the calculate () function accesses shared variables without holding mutexes This should not really be done This is a version that handles it It also incorrectly checks for pthread _* () function, report errors and exit when problems occur This is dramatic but sufficient for testing code Can be in https://github.com/jleffler/soq/tree/master/src/libsoq Stderr found in H header and support source code stderr c. Error handling masks the operation of the code to some extent, but it is very similar to what was shown earlier The main changes are that the mutex is locked before entering the loop, unlocked after exiting the loop, unlocked before sleep and re locked after waking up

This code also uses random scores instead of a fixed score and random sub second sleep time. It has five threads instead of three threads It uses an array of control structures and initializes them as needed Printing seeds (current time) is a very good; It will allow you to recreate the random sequence used when upgrading your program to handle command - line arguments (the thread scheduling problem is still uncertain.)

Note that a single call to printf () improves the appearance of the output compared to the original triple call The original code can (and does) interleave partial lines from different threads Each printf () produces an entire line, which is no longer a problem You can check out flockfile () and its friends to see what's happening – the specification has a comprehensive declaration covering the rest of the I / O library functions

/* SO 4544-8840 Multithreaded C program - threads not terminating */

#include "stderr.h"     // https://github.com/jleffler/soq/tree/master/src/libsoq
#include <errno.h>
#include <math.h>
#include <pthread.h>
#include <stdarg.h>
#include <stdio.h>
#include <stdlib.h>
#include <time.h>
#include <unistd.h>

static pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;
static int scholarship = 4000;
static int total = 0;
static void *calculate(void *data);

enum { MAX_THREADS = 5 };
enum { MIN_PERCENT = 10,MAX_PERCENT = 25 };

struct Data
{
    char name[2];
    struct timespec doze;
    double fraction;
};

static inline double random_fraction(void)
{
    return (double)rand() / RAND_MAX;
}

static inline _Noreturn void err_ptherror(int rc,const char *fmt,...)
{
    errno = rc;
    va_list args;
    va_start(args,fmt);
    err_print(ERR_SYSERR,ERR_STAT,fmt,args);
    va_end(args);
    exit(EXIT_FAILURE);
}

int main(int argc,char **argv)
{
    err_setarg0(argv[argc-argc]);
    pthread_t tids[MAX_THREADS];
    struct Data ctrl[MAX_THREADS];
    unsigned seed = time(0);
    printf("Seed: %u\n",seed);
    srand(seed);
    int rc;

    for (int i = 0; i < MAX_THREADS; i++)
    {
        ctrl[i].name[0] = 'A' + i;
        ctrl[i].name[1] = '\0';
        ctrl[i].doze.tv_sec = 0;
        ctrl[i].doze.tv_nsec = 100000000 + 250000000 * random_fraction();
        ctrl[i].fraction = (MIN_PERCENT + (MAX_PERCENT - MIN_PERCENT) * random_fraction()) / 100;
        if ((rc = pthread_create(&tids[i],&ctrl[i])) != 0)
            err_ptherror(rc,"Failed to create thread %d\n",i);
    }

    for (int i = 0; i < MAX_THREADS; i++)
    {
        if ((rc = pthread_join(tids[i],NULL)) != 0)
            err_ptherror(rc,"Failed to join thread %d\n",i);
    }

    printf("Total given out: %d\n",total);

    return 0;
}

static void *calculate(void *arg)
{
    struct Data *data = arg;
    printf("Thread %s: doze = 0.%03lds,fraction = %.3f\n",data->doze.tv_nsec / 1000000,data->fraction);
    int rc;
    if ((rc = pthread_mutex_lock(&mutex)) != 0)
        err_ptherror(rc,"Failed to lock mutex (1) in %s()\n",__func__);
    while (scholarship > 0)
    {
        if ((rc = pthread_mutex_unlock(&mutex)) != 0)
            err_ptherror(rc,"Failed to unlock mutex (1) in %s()\n",__func__);
        nanosleep(&data->doze,NULL);
        if ((rc = pthread_mutex_lock(&mutex)) != 0)
            err_ptherror(rc,"Failed to lock mutex (2) in %s()\n",__func__);
        double result = ceil(scholarship * data->fraction);
        total += result;
        scholarship -= result;
        if (result >= 1)
            printf("%s = %.2f\n",result);
    }
    if ((rc = pthread_mutex_unlock(&mutex)) != 0)
        err_ptherror(rc,"Failed to unlock mutex (2) in %s()\n",__func__);
    printf("Thread %s exited\n",data->name);

    return 0;
}

You can still modify the code to check the scholarship amount after sleep, breaking the infinite loop in the loop body These changes are left to the reader as a small exercise

Sample run

Seed: 1501727930
Thread A: doze = 0.119s,fraction = 0.146
Thread B: doze = 0.199s,fraction = 0.131
Thread C: doze = 0.252s,fraction = 0.196
Thread D: doze = 0.131s,fraction = 0.102
Thread E: doze = 0.198s,fraction = 0.221
A = 584.00
D = 349.00
E = 678.00
B = 314.00
A = 303.00
C = 348.00
D = 146.00
A = 187.00
D = 112.00
E = 217.00
B = 100.00
A = 97.00
C = 111.00
D = 47.00
E = 90.00
A = 47.00
B = 36.00
D = 24.00
A = 31.00
C = 36.00
D = 15.00
E = 29.00
B = 13.00
A = 13.00
D = 8.00
A = 10.00
E = 13.00
B = 6.00
C = 8.00
D = 3.00
A = 4.00
D = 3.00
E = 4.00
B = 2.00
A = 2.00
C = 2.00
D = 1.00
A = 2.00
E = 2.00
B = 1.00
A = 1.00
D = 1.00
Thread D exited
Thread C exited
Thread A exited
Thread E exited
Thread B exited
Total given out: 4000
The content of this article comes from the network collection of netizens. It is used as a learning reference. The copyright belongs to the original author.
THE END
分享
二维码
< <上一篇
下一篇>>