构建多线程的关键部分

时间:2014-06-30 19:14:25

标签: c multithreading file producer-consumer critical-section

我有一个生产者线程,它从源文件中读取每个char及其偏移量,然后将其写入共享循环缓冲区。

我还有一个消费者线程,它读取缓冲区中最旧的元素,然后将其写入复制文件。

生产者和消费者线程的数量作为命令行参数给出,每次生产者/消费者线程读取或写入缓冲区时,或者读取或写入文件时,特定行都写入日志文件。

现在我对生产者和消费者线程有一个关键部分。我如何构建它以便最大限度地减少在关键部分花费的时间(它变慢)。我在考虑为我的生产者和消费者线程提供多个关键部分。例如,在我的生产者线程中,我可以有一个关键部分,用于从源文件中读取并将特定行(ex"从文件"中读取)写入日志文件,以及另一个用于写入缓冲区的关键部分并将特定行(ex"写入缓冲区")写入日志文件。但是,如果关键部分是一个接一个,这又如何产生影响呢?

这是我的制作人主题:

void *INthread(void *arg)
{
    printf("INSIDE INthread\n");
    FILE *srcFile = (FILE*)arg;
    FILE *lp; // Log file pointers.
    int t_id = INid++; // Thread number.
    int curOffset;
    BufferItem result;
    struct timespec t;

    t.tv_sec = 0;
    t.tv_nsec = rand()%(TEN_MILLIS_IN_NANOS+1);
    nanosleep(&t, NULL);

    fseek(srcFile, 0, SEEK_CUR);
    curOffset = ftell(srcFile); // Save the current byte offset.

    fseek(srcFile, 0, SEEK_END);
    if(len == 0) // If len hasnt been set by the first IN thread yet.
        len = ftell(srcFile); // Save the length of the srcFile (number of chars).

    fseek(srcFile, curOffset, SEEK_SET); // Revert file pointer to curOffset.
    printf("ID %d number of bytes %d\n", t_id, len);

    int offs;
    int ch;
    while(len > 0) // Go through each byte/char in file.
    {

        /*** CRITICAL SECTION ********************************/
        sem_wait(&empty); /* acquire the empty lock */
        pthread_mutex_lock( &pt_mutex );
        if(len > 0){
            fseek(srcFile, 0, SEEK_CUR);
            if((offs = ftell(srcFile)) != -1)
                result.offset = offs;     /* get position of byte in file */
            if((ch = fgetc(srcFile)) != EOF)
                result.data = ch;       /* read byte from file */

            // Write to log file "read_byte PTn Ox Bb I-1".
            if (!(lp = fopen(log, "a"))) {
                printf("could not open log file for writing");
            }
            if(fprintf(lp, "read_byte PT%d O%d B%d I-1\n", t_id, offs, ch) < 0){
                printf("could not write to log file");
            }
            printf("ID %d --- offset %d char %c len%d\n", t_id, result.offset, result.data, len);
            addItem(&cBuff, &result);

            // Write to log file "produce PTn Ox Bb Ii  ".
            if(fprintf(lp, "produce PT%d O%d B%d I%d\n", t_id, offs, ch, cBuff.lastInd) < 0){
                printf("could not write to log file");
            }
            fclose(lp);

            len--;
        }
        pthread_mutex_unlock( &pt_mutex );
        sem_post(&full); /* signal full */
        /*** END CRITICAL SECTION ********************************/

        t.tv_sec = 0;
        t.tv_nsec = rand()%(TEN_MILLIS_IN_NANOS+1);
        nanosleep(&t, NULL);
    }

    inJoin[t_id] = 1; // This IN thread is ready to be joined.
    printf("EXIT INthread\n");
    pthread_exit(0);
}

这是我的消费者主题:

void *OUTthread(void *arg)
{
    printf("INSIDE OUTthread\n");
    struct timespec t;
    t.tv_sec = 0;
    t.tv_nsec = rand()%(TEN_MILLIS_IN_NANOS+1);
    nanosleep(&t, NULL);

    int processing = 1;
    FILE *targetFile, *lp;
    BufferItem OUTresult;
    int t_id = OUTid++;
    int offs, ch;
    int numBytes = len;

    while(processing){

        /*** CRITICAL SECTION ********************************/
        sem_wait(&full); /* acquire the full lock */
        pthread_mutex_lock( &pt_mutex );
        cbRead(&cBuff, &OUTresult);
        offs = OUTresult.offset;
        ch = OUTresult.data;

        if (!(lp = fopen(log, "a"))) {
            printf("could not open log file for writing");
        }
        // Write to log file "consume CTn Ox Bb Ii".
        if(fprintf(lp, "consume CT%d O%d B%d I%d\n", t_id, offs, ch, cBuff.lastInd) < 0){
            printf("could not write to log file");
        }

        printf("From buffer: offset %d char %c\n", OUTresult.offset, OUTresult.data);
        if (!(targetFile = fopen(arg, "r+"))) {
            printf("could not open output file for writing");
        }
        if (fseek(targetFile, OUTresult.offset, SEEK_SET) == -1) {
            fprintf(stderr, "error setting output file position to %u\n",
                    (unsigned int) OUTresult.offset);
            exit(-1);
        }
        if (fputc(OUTresult.data, targetFile) == EOF) {
            fprintf(stderr, "error writing byte %d to output file\n", OUTresult.data);
            exit(-1);
        }

        // Write to log file "write_byte CTn Ox Bb I-1".
        if(fprintf(lp, "write_byte CT%d O%d B%d I-1\n", t_id, offs, ch) < 0){
            printf("could not write to log file");
        }
        fclose(lp);
        fclose(targetFile);

        pthread_mutex_unlock( &pt_mutex );
        sem_post(&empty); /* signal empty */
        /*** END CRITICAL SECTION ********************************/

        t.tv_sec = 0;
        t.tv_nsec = rand()%(TEN_MILLIS_IN_NANOS+1);
        nanosleep(&t, NULL);

    }

    outJoin[t_id] = 1; // This OUT thread is ready to be joined.
    printf("EXIT OUTthread\n");
    pthread_exit(0);

}

1 个答案:

答案 0 :(得分:1)

我会对此进行整理,以便您在代码中采用以下形式:

... processing ...
mutex lock
resource read / write
mutex unlock
... continue processing

围绕要共享的每个资源。因此,最终会有多个互斥锁,一个用于生成器读取文件,一个用于生成器写入循环缓冲区。一个是消费者从循环缓冲区读取...分机每个人都会将一个读/写操作封装到他们尊重的资源中。

我也会这样做,以便你的循环缓冲区不能覆盖自身,否则你将无法同时读取和写入缓冲区,只能为读/写操作提供一个互斥锁有效地增加了消费者和生产者线程的等待时间。

这可能不是最好的&#39;解决方案,但它应该更快地处理,然后围绕大部分代码进行巨大的锁定。