简体   繁体   中英

Confusion with thread synchronization [pthreads]

I have had a problem threads for a long time. This code is supposed to have a worker thread increment the value of a shared integer while the main thread prints it out. However, I am not getting my expected output.

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

pthread_mutex_t lock;
int shared_data = 0; //shared data


// Often shared data is more complex than just an int.
void* thread_function(void* arg)
{
    int i;
    for (i = 0; i < 10; ++i)
    {
        // Access the shared data here.
        pthread_mutex_lock(&lock);
        shared_data++;
        pthread_mutex_unlock(&lock);
    }

    return NULL;
}

int main(void)
{
    pthread_t thread;
    int i;
    void* exit_status;
    // Initialize the mutex before trying to use it.
    pthread_mutex_init(&lock, NULL);
    pthread_create(&thread, NULL, thread_function, NULL);
    // Try to use the shared data.
    for (i = 0; i < 10; ++i)
    {
        sleep(1);
        pthread_mutex_lock(&lock);
        printf ("\r for i= %d Shared integer 's value = %d\n", i, shared_data);
        pthread_mutex_unlock(&lock);
    }
    printf("\n");
    pthread_join(thread, &exit_status);
    // Clean up the mutex when we are finished with it.
    pthread_mutex_destroy(&lock);

    return 0;
}

Here is what I expect:

for i=0 Shared Integer 's value = 0
for i=1 Shared Integer 's value = 1 
for i=3 Shared Integer 's value = 2
...
for i=10 Shared Integer 's value =10

but the result is:

for i=0 Shared Integer 's value = 0
for i=1 Shared Integer 's value = 10
for i=3 Shared Integer 's value = 10
...
for i=10 Shared Integer 's value =10

so how can i resoleve this?

The main thread and your worker thread are running concurrently. That is, getting those for loops to coincide with each other perfectly is nearly impossible without extra synchronization.

Your output is exactly what you should expect. The time taken to spawn the thread allows the main thread to print before the other thread changes the shared data. Then, the print takes so long that the other thread completely finishes with its loop and increments the shared data to 10 before the main thread can get to its second iteration.

In a perfect world, this little hack using condition variables will get you what you want: EDIT: condition variables were a bad idea for this. Here is working version that uses pseudo atomic variables and doesn't contain UB :) :

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

pthread_mutex_t want_incr_mut;
pthread_mutex_t done_incr_mut;
int want_incr = 0;
int done_incr = 0;
int shared_data = 0; //shared data

// Not using atomics, so...

void wait_for_want_increment()
{
    while (1)
    {
        pthread_mutex_lock(&want_incr_mut);
        if (want_incr)
        {
            pthread_mutex_unlock(&want_incr_mut);
            return;
        }
        pthread_mutex_unlock(&want_incr_mut);
    }
}

void wait_for_done_incrementing()
{
    while (1)
    {
        pthread_mutex_lock(&done_incr_mut);
        if (done_incr)
        {
            pthread_mutex_unlock(&done_incr_mut);
            return;
        }
        pthread_mutex_unlock(&done_incr_mut);
    }
}


void done_incrementing()
{
    pthread_mutex_lock(&done_incr_mut);

    done_incr = 1;
    pthread_mutex_lock(&want_incr_mut);
    want_incr = 0;
    pthread_mutex_unlock(&want_incr_mut);

    pthread_mutex_unlock(&done_incr_mut);
}

void want_increment()
{
    pthread_mutex_lock(&want_incr_mut);

    want_incr = 1;
    pthread_mutex_lock(&done_incr_mut);
    done_incr = 0;
    pthread_mutex_unlock(&done_incr_mut);

    pthread_mutex_unlock(&want_incr_mut);
}

// Often shared data is more complex than just an int.
void* thread_function(void* arg)
{
    int i;
    for (i = 0; i < 10; ++i)
    {
        wait_for_want_increment();
        // Access the shared data here.
        shared_data++;
        done_incrementing();
    }

    return NULL;
}

int main(void)
{
    pthread_t thread;
    int i;
    void* exit_status;

    // Initialize the mutex before trying to use it.
    pthread_mutex_init(&want_incr_mut, NULL);
    pthread_mutex_init(&done_incr_mut, NULL);
    pthread_create(&thread, NULL, thread_function, NULL);

    // Try to use the shared data.
    for (i = 0; i <= 10; ++i)
    {
        printf("\r for i= %d Shared integer 's value = %d\n", i, shared_data);
        if (i == 10) break;

        want_increment();
        wait_for_done_incrementing();
    }
    printf("\n");
    pthread_join(thread, &exit_status);
    // Clean up the mutexes when we are finished with them.
    pthread_mutex_destroy(&want_incr_mut);
    pthread_mutex_destroy(&done_incr_mut);

    return 0;
}

Here, we just tell the worker that we want an increment and wait for him to say he is done before we continue. Meanwhile, the worker waits for us to want an increment and tells us when he is done.

I also changed the main loop to go to ten because that is what I think you want.

Here is my output:

for i= 0 Shared integer 's value = 0
for i= 1 Shared integer 's value = 1
for i= 2 Shared integer 's value = 2
for i= 3 Shared integer 's value = 3
for i= 4 Shared integer 's value = 4
for i= 5 Shared integer 's value = 5
for i= 6 Shared integer 's value = 6
for i= 7 Shared integer 's value = 7
for i= 8 Shared integer 's value = 8
for i= 9 Shared integer 's value = 9
for i= 10 Shared integer 's value = 10

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