简体   繁体   中英

C in printing function mutex does not work properly

I am trying to give an id to threads and then I want to print each threads id that I have given, but there is a situation about mutex I guess, I think I am handling the critical section but it seems I could not.

#include <pthread.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
pthread_mutex_t m = PTHREAD_MUTEX_INITIALIZER;

struct info{
    int id;
    int flag;
};

void *print_info(void* arg){
    struct info *arg_struct = (struct info*) arg;

    pthread_mutex_lock(&m);

    printf("%d ", arg_struct->id);
    pthread_mutex_unlock(&m);

    pthread_exit(0);

}
int main(int argc, char *argv[])
{
    int number = 10;
    pthread_t tid[number];
    for (int i = 0; i < number; ++i) {
        int info[2];
        info[0] = i;
        info[1] = 0;

        pthread_create(&tid[i], NULL, print_info, &info);
    }
    for (int i = 0; i < number; ++i) {
        pthread_join(tid[i], NULL);
    }

    return 0;
}

Here is the output: 1 2 3 4 5 6 7 8 9 9

Each time I execute it differ but more or less the concept is the same is does not print some values and it prints some more than once.

But the expected output is this: 0 1 2 3 4 5 6 7 8 9

[or something not in order but each value is exactly printed once I guess] Thanks

Giving each thread its own control data

As written, your code cannot guarantee the sequence of the output numbers — the order in which the threads execute depends on the o/s and the hardware. You can easily make sure that each ID is printed just once, by making sure that each thread has its own struct info to work with. Your existing code (a) type-puns an array with a structure, which is a bad idea ( alk 's comment ), and (b) reuses the same space on the main program's stack so that by the time the threads are at work, it is feasible for the main loop to have changed the values stored.

This is what @ rafix07 said in a comment . You claimed to have tried the fix without success. I have to conclude that your amended code didn't do what was necessary.

You need to use code more like this (which also prints a newline at the end of each run):

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

pthread_mutex_t m = PTHREAD_MUTEX_INITIALIZER;

struct info
{
    int id;
    int flag;
};

static void *print_info(void *arg)
{
    struct info *arg_struct = (struct info *)arg;

    pthread_mutex_lock(&m);

    printf("%d ", arg_struct->id);
    pthread_mutex_unlock(&m);

    pthread_exit(0);
}

int main(void)
{
    int number = 10;
    pthread_t tid[number];
    struct info info[number];
    for (int i = 0; i < number; ++i)
    {
        info[i].id = i;
        info[i].flag = 0;
        pthread_create(&tid[i], NULL, print_info, &info[i]);
    }

    for (int i = 0; i < number; ++i)
    {
        pthread_join(tid[i], NULL);
    }
    putchar('\n');

    return 0;
}

When I ran it 10 times in a row, I got the outputs:

0 7 2 8 3 1 4 9 5 6 
1 8 0 9 2 3 6 5 4 7 
0 4 5 2 3 6 7 1 8 9 
0 1 2 3 4 5 6 7 8 9 
2 7 0 3 5 6 4 8 1 9 
2 0 7 6 3 5 4 8 9 1 
0 9 1 3 5 6 7 2 8 4 
0 7 1 8 4 3 9 2 5 6 
0 7 1 8 3 5 4 2 9 6 
0 3 4 5 6 1 2 8 7 9 

As you can see, each of the numbers 0..9 appears once in each line of output, but the sequence in which the threads execute is anything but determinate.

I'm not convinced that the mutex is buying you anything. All I/O functions such as printf() must behave as if they use flockfile() on entry and funlockfile() on return.

Tested on a MacBook Pro running macOS Mojave 10.14.6 (don't ask!) with GCC 9.2.0.

Guaranteeing sequence

A simple modification ensures the sequence — the main thread locks the mutex before launching a thread, and the thread unlocks it before exiting. However, this means there is no meaningful concurrency — you might as well write:

for (int i = 0; i < 10; i++)
    printf("%d ", i);
putchar('\n');

This would avoid all the overhead of starting, running, and cleaning up after the threads.

The revised code simply moves one line from the print_info() function into main() :

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

pthread_mutex_t m = PTHREAD_MUTEX_INITIALIZER;

struct info
{
    int id;
    int flag;
};

static void *print_info(void *arg)
{
    struct info *arg_struct = (struct info *)arg;

    printf("%d ", arg_struct->id);
    pthread_mutex_unlock(&m);

    pthread_exit(0);
}

int main(void)
{
    int number = 10;
    pthread_t tid[number];
    struct info info[number];
    for (int i = 0; i < number; ++i)
    {
        info[i].id = i;
        info[i].flag = 0;
        pthread_mutex_lock(&m);
        pthread_create(&tid[i], NULL, print_info, &info[i]);
    }

    for (int i = 0; i < number; ++i)
    {
        pthread_join(tid[i], NULL);
    }
    putchar('\n');

    return 0;
}

And the output:

0 1 2 3 4 5 6 7 8 9 
0 1 2 3 4 5 6 7 8 9 
0 1 2 3 4 5 6 7 8 9 
0 1 2 3 4 5 6 7 8 9 
0 1 2 3 4 5 6 7 8 9 
0 1 2 3 4 5 6 7 8 9 
0 1 2 3 4 5 6 7 8 9 
0 1 2 3 4 5 6 7 8 9 
0 1 2 3 4 5 6 7 8 9 
0 1 2 3 4 5 6 7 8 9 

It fails because you keep passing a pointer to what is effectively the same vector to the child threads, and promptly overwriting the contents in the main thread while the child threads are running.

You could equally well get an output of all 9s given a loaded system. Or even the numbers in reverse (only because I don't remmember there being a guarantee anywhere that threads will be scheduled in the same order as you create them). Or a very very strange number because if the child threads don't start running till the main thread starts the join, whatever 'arg' points to will be in the middle of the pthread_join call stack.

All the mutex protects you from is the threads outputting to stdout at the same time.

In passing, casting an array of ints to a struct of 2 ints is undefined behaviour, which is a whole other can of worms.

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