为什么我的多线程程序有时会阻塞?

时间:2014-05-20 18:44:15

标签: c linux pthreads mutex

我们必须编写一个程序,它有2个线程。其中一个按令牌读取内容令牌并将它们存储到一个数组中。另一个从数组中读取标记并将其写入文件。 这是代码:

#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];
}

如果我想运行该程序,它有时会阻止,但我不知道为什么。但是如果程序终止,则outputfile与inputfile相同。 有人可以解释一下,为什么会发生这种情况?

2 个答案:

答案 0 :(得分:3)

代码存在许多问题,但锁定的原因很可能是您在没有锁定互斥锁的情况下发出信号,并且在没有锁定互斥锁的情况下检查您的情况。两者都是必要的,以确保您不会丢失信号。

如无用所述,请确保您知道共享变量是什么,并且必要时它们是互斥保护的。例如,您的buf_pos在读者线程中被修改后没有保护,并且在两个线程中用作等待而没有互斥保护的条件。

此外,在执行pthread_cond_wait时,您通常需要一个守护表达式,以确保您不会对所谓的spurios唤醒做出反应(请查看&#34;条件等待语义&#34;在http://linux.die.net/man/3/pthread_cond_wait)部分,并确保您在等待测试之前实际发生的情况并开始等待。

例如,在你的作家帖子中你可以这样做:

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

但是,请仔细查看您的程序,确定您的共享变量,并确保它们在必要时受到互斥保护。此外,您只需要互斥保护共享数据以进行写访问,这远非正确。在某些情况下,您可以在阅读共享变量时删除互斥保护,但是您必须分析您的代码以确保实际情况如此。

答案 1 :(得分:3)

你的死锁问题是你有两个线程都这样做:

pthread_mutex_lock( &mutex );
pthread_cond_wait( &condition, &mutex );
pthread_mutex_unlock( &mutex ); 
相同的互斥锁上,以及相同的条件变量。假设您的读取线程在写入线程之前立即执行这三行。他们都将永远等待相同的条件变量。请记住,如果当时没有线程实际等待它,则发出条件变量的信号。现在你只有当你的一个线程等待那个变量时,你才会避免死锁,如果它偶然发生在它等待它之前发出信号的话。

Sonicwave的答案在识别代码中的其他问题方面做得很好,但总的来说,你对保护共享数据不够小心,而且你没有正确使用条件变量,所以你的线程没有正确同步彼此。除此之外,如果你的一个线程正在等待一个条件变量,你需要确保在另一个线程没有接收到信号并唤醒之前,其他线程不会在相同或不同的线程上等待。