记录线程的奇怪行为

时间:2016-03-21 02:26:02

标签: c multithreading pthreads

我创建了以下程序来创建日志记录线程,并允许其他线程写入由此线程管理的共享缓冲区。奇怪的是,如果我在stopLog函数中不包含对日志的写入,则日志永远不会关闭,并且线程不会终止。

#include "log.h"
#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include <pthread.h>

#define BUFFER_SIZE 128

static pthread_mutex_t logFileMutex = PTHREAD_MUTEX_INITIALIZER;
static pthread_mutex_t logInUse = PTHREAD_MUTEX_INITIALIZER;
static pthread_cond_t emptyCond = PTHREAD_COND_INITIALIZER;
static pthread_cond_t fullCond = PTHREAD_COND_INITIALIZER;
static int bufferIsEmpty;
static int bufferIsFull;
static char buffer[BUFFER_SIZE];
static int in;
static int out;
static int count;
static int stop;

void * logOutput(void * in) {
    stop = 0;
    bufferIsEmpty = 1;
    bufferIsFull = 0;
    char ch;
    FILE * f = fopen((char *)in, "a");
    if (NULL != f) {
        fprintf(f, "---------------- Log Opened ----------------\n");
        while (!stop) {
            pthread_mutex_lock(&logFileMutex);
            while (bufferIsEmpty)
                pthread_cond_wait(&emptyCond, &logFileMutex);
            ch = buffer[out++];
            out %= BUFFER_SIZE;
            if (--count == 0)
                bufferIsEmpty = 1;
            bufferIsFull = 0;
            fputc(ch, f);
            pthread_cond_signal(&fullCond);
            pthread_mutex_unlock(&logFileMutex);
        }
        fprintf(f, "---------------- Log Closed ----------------\n");
        fflush(f);
        fclose(f);
    } else {
        fprintf(stderr, "Error opening log file %s\n", (char *)in);
    }
    pthread_exit(NULL);
}

void writeToLog(char * str) {
    pthread_mutex_lock(&logInUse);
    for (int i = 0; i <= strlen(str); i++) {
        pthread_mutex_lock(&logFileMutex);
        while (bufferIsFull)
            pthread_cond_wait(&fullCond, &logFileMutex);
        if (strlen(str) == i)
            buffer[in++] = '\n';
        else
            buffer[in++] = str[i];
        in %= BUFFER_SIZE;
        if (++count == (BUFFER_SIZE - 1))
            bufferIsFull = 1;
        bufferIsEmpty = 0;
        usleep(10);
        pthread_cond_signal(&emptyCond);
        pthread_mutex_unlock(&logFileMutex);
    }
    pthread_mutex_unlock(&logInUse);
}

void stopLog() {
    writeToLog("Stopping log...");
    stop = 1;
}

1 个答案:

答案 0 :(得分:1)

如果缓冲区为空且logOutput()pthread_cond_wait()被阻止,则设置stop = 1不会将其唤醒。在下次唤醒之前它不会注意到,写一些日志输出就可以实现。

作为线程之间共享的变量,还需要保护stop不受同步访问的影响。

您可以通过将logOutput()中的循环更改为

来解决这两个问题
pthread_mutex_lock(&logFileMutex);
while (!stop)
{
    while (!stop && bufferIsEmpty)
        pthread_cond_wait(&emptyCond, &logFileMutex);

    while (!bufferIsEmpty)
    {
        ch = buffer[out++];
        out %= BUFFER_SIZE;
        if (--count == 0)
            bufferIsEmpty = 1;
        fputc(ch, f);
    }
    bufferIsFull = 0;
    pthread_cond_signal(&fullCond);
}
pthread_mutex_unlock(&logFileMutex);

并将stopLog()更改为:

void stopLog() {
    pthread_mutex_lock(&logFileMutex);
    stop = 1;
    pthread_cond_signal(&emptyCond);
    pthread_mutex_unlock(&logFileMutex);
}

现在,当调用stopLog()时,logOutput()将被唤醒,处理在停止和退出的同时发送的任何日志消息。