简体   繁体   中英

Why does my multi-threaded program blocks sometimes?

We have to write a program, which has 2 threads. One of them reads the content token by token and stores them into a array. The other reads the tokens from the array and writes it into a file. Here is the code:

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

#define MAX 10


int buffer[MAX];
int buf_pos;    // the actual position of the buffer

void copy();

int flag;       // if flag is 0, the whole content of the file was read
FILE *source;
FILE *dest;

// read the content of a file token by token and stores it into a buffer
void *read_from_file();

// write the content of a buffer token by token into a file
void *write_to_file();

pthread_cond_t condition = PTHREAD_COND_INITIALIZER;
pthread_mutex_t mutex;

int main( int argc, char *argv[] )
{
    flag = 1;
    pthread_t writer;
    pthread_t reader;
    pthread_mutex_init( &mutex, NULL );

        if( argc != 3 )
        {
            printf( "Error\n" );
            exit( EXIT_FAILURE );
        }

        source = fopen( argv[1], "r" );
        dest = fopen( argv[2], "w" );
        if( source == NULL || dest == NULL )
        {
            printf( "Error\n" );
            exit( EXIT_FAILURE );
        }

    pthread_create( &reader, NULL, read_from_file, NULL );
    pthread_create( &writer, NULL, write_to_file, NULL );

    pthread_join( reader, NULL );
    pthread_join( writer, NULL );

}

void *read_from_file()
{
    int c;
    while( ( c = getc( source ) ) != EOF )
    {
        if( buf_pos < MAX - 1 ) // the buffer is not full
        {
            pthread_mutex_lock( &mutex );
            buffer[buf_pos++] = c;
            pthread_mutex_unlock( &mutex );
            pthread_cond_signal( &condition );
        }
        else
        {
            buffer[buf_pos++] = c;  // store the last token
            pthread_mutex_lock( &mutex );
            pthread_cond_wait( &condition, &mutex );    // wait until the other thread sends a signal
            pthread_mutex_unlock( &mutex ); 
        }
    }
    flag = 0;   // EOF
    return NULL;
}


void *write_to_file()
{
    int c;
    while( flag || buf_pos > 0 )
    {
        if( buf_pos > 0 )   // The buffer is not empty
        {
            fputc( buffer[0], dest );   // write the first token into file
            pthread_mutex_lock( &mutex );
            copy();
            --buf_pos;
            pthread_mutex_unlock( &mutex );
            pthread_cond_signal( &condition );
        }
        else
        {
            pthread_mutex_lock( &mutex );
            pthread_cond_wait( &condition, &mutex );
            pthread_mutex_unlock( &mutex );
        }
    }
    return NULL;
}

void copy()
{
    int i = 0;
    for( ; i < buf_pos - 1; ++i )
        buffer[i] = buffer[i + 1];
}

If I want to run the program, it sometimes blocks, but i do not know why. But if the program terminates, the outputfile is the same as the inputfile. Can somebody explain me, why this could happen?

There are a number of problems with the code, but the reason for your lockups is most probably that you send out signals without the mutex locked, and without checking your condition with the mutex locked. Both are necessary to make sure that you don't end up with lost signals.

As noted by Useless, make sure that you know what your shared variables are, and that they are mutex-protected where necessary. For instance, your buf_pos is modified without protection in your reader thread, and used as a condition for waiting without mutex-protection in both threads.

Also, when doing pthread_cond_wait , you typically want a guard-expression to make sure you don't react to socalled spurios wakeups (have a look at the "Condition Wait Semantics" section at http://linux.die.net/man/3/pthread_cond_wait ), and to make sure that the condition you're waiting for hasn't actually happened between testing for it, and starting the wait.

For instance, in your writer thread you could do:

pthread_mutex_lock(&mutex);
while(buf_pos == 0) pthread_cond_wait(&condition, &mutex);
pthread_mutex_unlock(&mutex);

But still, have a thorough look at your program, identify your shared variables, and make sure that they are mutex-protected where necessary. Also, it's far from correct that you only need to mutex-protect shared data for write access. In some cases you can get away with removing the mutex-protection when reading shared variables, but you'll have to analyze your code to make sure that that's actually the case.

Your problem with the deadlock is that you have both threads do this:

pthread_mutex_lock( &mutex );
pthread_cond_wait( &condition, &mutex );
pthread_mutex_unlock( &mutex ); 

on the same mutex, and the same condition variable. Suppose your reading thread executes those three lines immediately before your writing thread does. They're both going to be waiting on the same condition variable forever. Remember, signaling a condition variable does nothing if there are no threads actually waiting on it at the time. Right now you only avoid deadlock when one of your threads waits on that variable if, by chance, it happens to signal it before waiting for it itself.

Sonicwave's answer does a good job of identifying the other problems in your code, but in general you're not being careful enough about protecting your shared data, and you're not using condition variables properly so your threads are not properly synchronizing with each other. Among other things, if one of your threads is waiting on a condition variable, you need to make sure that there are no circumstances in which the other thread can wait on the same or different one before the other one receives a signal and wakes up.

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