Тупик с многопоточностью в C - PullRequest
0 голосов
/ 31 мая 2018

Я новичок в C и имею следующий многопоточный сценарий, где N-потоки читают из одного двоичного файла и записывают в свои собственные отдельные файлы, например.поток 1 записывает в файл_1, а поток 2 записывает в файл_2 и т. д.

Это работает для потоков ~ 2/3, но больше не может зайти в тупик, но я не уверен, каков источникпроблема есть.

Ура!:)

#include "tape.h"
#include <stddef.h>
#include <stdio.h>
#include <string.h>
#include <pthread.h>
#define BYTE unsigned char

int present_file = 0;
pthread_t THREADS[100];
int OFFSET[100];
FILE * file;
pthread_mutex_t read_mutex = PTHREAD_MUTEX_INITIALIZER;
pthread_mutex_t finished_read_mutex = PTHREAD_MUTEX_INITIALIZER;
pthread_mutex_t file_read = PTHREAD_MUTEX_INITIALIZER;
pthread_cond_t read_cond = PTHREAD_COND_INITIALIZER;
pthread_cond_t finished_read_cond = PTHREAD_COND_INITIALIZER;
int read = 0;
int finished_reading = 0;
int read_value;
size_t file_length;
int n_threads = 0;

void* tape_reader(void* args) {

int offset = *((int *) args);

FILE * file_ptr;

if(present_file == 1){
    file_ptr = fopen("head1", "wb");
}

else if (present_file == 2){
    file_ptr = fopen("head2", "wb");
}

else if (present_file == 3){
    file_ptr = fopen("head3", "wb");
}

else if (present_file == 4){
    file_ptr = fopen("head4", "wb");
}

else if (present_file == 5){
    file_ptr = fopen("head5", "wb");
}

else if (present_file == 6){
    file_ptr = fopen("head6", "wb");
}

else if (present_file == 7){
    file_ptr = fopen("head7", "wb");
}

else if (present_file == 8){
    file_ptr = fopen("head8", "wb");
}

else if (present_file == 9){
    file_ptr = fopen("head9", "wb");
}
else if (present_file == 10){
    file_ptr = fopen("head10", "wb");
}
else{
    file_ptr = NULL;
}

while(read == 0){
    pthread_cond_wait(&read_cond, &read_mutex);
}

//thread start reading

pthread_mutex_lock(&file_read);

BYTE buffer;

rewind(file);

fseek(file, offset, SEEK_SET);

for(int i = 0; i < read_value; i++){

    if(i == file_length){
        i = 0;
    }
    fread(&buffer, 1, sizeof(buffer), file);
    fwrite(&buffer, 1, sizeof(buffer), file_ptr);
}

fclose(file_ptr);

pthread_mutex_unlock(&file_read);

return 0;

}


int main(int argc, char** argv) {

if(argc == 1){
    printf("Tape Not Inserted\n");
    return 1;
}

else if(argc == 2){

    file = fopen(argv[1], "rb");

    if(file == NULL){
        printf("Cannot Read Tape\n");
        return 1;
    }
    else{

        fseek(file, 0, SEEK_END);

        file_length = ftell(file);

        rewind(file);

        while(1){
            char userInput[100];

            int offset;

            scanf("%s", userInput);

            if(strcmp(userInput, "QUIT") == 0){
                return 0;
            }

            //printf("%s\n", userInput);

            if(strcmp(userInput, "HEAD") == 0){

                scanf("%d", &offset);

                pthread_create(THREADS + present_file, NULL, tape_reader, (void *) &offset);

                n_threads++;

                present_file++;

                if(offset >= 0){
                    printf("HEAD %d at +%d\n", present_file, offset);
                    printf("\n");
                }
                else{
                    printf("HEAD %d at %d\n", present_file, offset);
                    printf("\n");

                }

            }


            if(strcmp(userInput, "READ") == 0){

                int read_for;

                scanf("%d", &read_for);

                read_value = read_for;

                read = 1;

                pthread_cond_signal(&read_cond);

                //threads read

                printf("Finished Reading\n\n");

                for(int i = 0; i < n_threads; i++){
                    pthread_join(THREADS[i], NULL);
                }

                return 0;
            }

        }

    }

}
}

Ответы [ 2 ]

0 голосов
/ 31 мая 2018

Помимо проблем, о которых сказал Джереми , есть еще одна хитрая проблема. Вы не можете гарантировать, что present_file в потоке совпадает со значением, которое вы установили снаружи. Этот код:

      if(strcmp(userInput, "HEAD") == 0){

            scanf("%d", &offset);

            pthread_create(THREADS + present_file, NULL, tape_reader, (void *) &offset);

            n_threads++;

            present_file++;

            ...

        }

Вы не знаете, когда поток действительно запускается. Так, например, в 1-й итерации она может начинаться после увеличения present_file в main, а во 2-й итерации она начинается до увеличения present_file.Тогда у вас будет аналогичный present_file для 2 разных потоков ведущий доступ к одному и тому же файлу .

Несмотря на то, что это очень сложно, и вы можете не увидеть в нормальных условиях, это может произойти,И поскольку вы говорите, что видите эту проблему для большего числа потоков, я думаю, что это будет основной причиной вашей проблемы.

Вы можете использовать блокировку доступа к ее значению, чтобы столкнуться с проблемой.Другой подход, который я лично предпочитаю, заключается в использовании выделенного struct для передачи как offset, так и present_file в поток, используя аргумент.

struct params
{
    int present_file;
    int offset;
};

...

      if(strcmp(userInput, "HEAD") == 0){

            scanf("%d", &offset);

            struct params *params = calloc(1, sizeof(*params));
            params->present_file = present_file;
            params->offset = offset;

            pthread_create(THREADS + present_file, NULL, tape_reader, (void *) params);

            n_threads++;

            present_file++;

            ...

        }

просто не забудьте free params в потоке после считывания значений с него.

0 голосов
/ 31 мая 2018

Я вижу по крайней мере две ошибки в этой части вашего кода:

while(read == 0){
   pthread_cond_wait(&read_cond, &read_mutex);
}

Ошибка # 1 заключается в том, что вы читаете значение глобальной переменной read без какой-либо сериализации.Поскольку read является int (а не типом atomic), выполнение этого вызывает неопределенное поведение (например, поток A может изменить значение read, а поток B может никогда не «увидеть» это изменение)

Ошибка №2: вы звоните pthread_cond_wait() без read_mutex, который уже находится в заблокированном состоянии.Как говорится в справочной странице pthread_cond_wait () :

Функция pthread_cond_wait () [используется] используется для блокировки условной переменной.Они вызываются с мьютексом, заблокированным вызывающим потоком, или может привести к неопределенному поведению.

Могут быть и другие проблемы, но это то, что бросается в глаза.

...