Как я могу прочитать файл от конца до начала с потоками C? - PullRequest
0 голосов
/ 30 апреля 2019

Мне нужно прочитать файл с 2 потоками, один из них будет читать файл с начала до середины, а другой - с конца до середины. У меня есть 10 чисел с плавающей запятой в моем файле. Первые 5 float будут суммироваться одним потоком, а последние 5 float будут суммироваться другим. с func1 все в порядке, но я не смог обработать часть func2.

float sum=0,sum1=0,sum2=0,flo;  
int i=0;
FILE *fp;

 void *func1(void *param) {
      for(i=1;i<=5;i++) {
      fscanf(fp,"%f",&flo);
      sum1=flo+sum1;
}
      pthread_exit(0);
}
int main() {

   pthread_t tid;   
   pthread_attr_t attr;   
   pthread_attr_init(&attr);

   fp = fopen("input.txt","r");
   pthread_create(&tid, &attr, func1,fp);
   pthread_create(&tid, &attr, func2,fp);
   pthread_join(tid, NULL);
}

1 Ответ

0 голосов
/ 30 апреля 2019

Безусловно, самый простой метод - использовать один поток для чтения. Однако, учитывая, что это упражнение по использованию потоков POSIX, тогда:

  • Вы всегда должны проверять возвращаемое значение из fscanf() - потоков или без потоков.
  • Вы должны избегать глобальных переменных даже в непоточных программах.
  • Поскольку потоки и i, sum1 и flo являются глобальными, вы настраиваете себя на ужасные условия гонки. Вы должны иметь мьютексы, чтобы предотвратить одновременный доступ к этим переменным. Или, лучше, сделайте их локальными для функции потока.
  • Вам также необходимо заставить функции возвращать соответствующее значение.
  • Вы должны использовать параметр, который передается в функцию потока.
  • Вы должны сохранить значения идентификатора потока в отдельных переменных, чтобы вы могли дождаться завершения обоих потоков; Ваш код ожидает только одного потока (второго).
  • Вы должны напечатать результат.
  • Вам не нужно беспокоиться о синхронизации в файловом потоке - POSIX требует, чтобы это произошло в любом случае (см. flockfile() и все его близкие родственники, документированные на одной странице).
  • Вы должны предоставить и func1(), и func2() для создания MCVE ( Минимальный, завершенный, проверяемый пример . Я предполагаю, что одна и та же функция будет выполнять для обоих потоков, что не рискованное предположение в контексте.

Соединение этого приводит к:

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

struct tcb
{
    FILE *fp;
    double sum;
    int thread_num;
};

static void *func1(void *param)
{
    struct tcb *tcb = param;
    double sum1 = 0.0, flo;
    for (int i = 1; i <= 5; i++)
    {
        if (fscanf(tcb->fp, "%lf", &flo) != 1)
        {
            fprintf(stderr, "Thread %d failed to read value %d\n", tcb->thread_num, i);
            pthread_exit(0);
        }
        sum1 += flo;
    }
    tcb->sum = sum1;
    pthread_exit(0);
}

int main(void)
{
    FILE *fp;
    pthread_t tid1;
    pthread_t tid2;
    struct tcb tcb[2];
    pthread_attr_t attr;

    pthread_attr_init(&attr);
    const char *filename = "input.txt";

    if ((fp = fopen(filename, "r")) == NULL)
    {
        fprintf(stderr, "Failed to open file %s for reading\n", filename);
        exit(1);
    }
    tcb[0].fp = fp;
    tcb[0].thread_num = 0;
    tcb[0].sum = 0.0;
    tcb[1].fp = fp;
    tcb[1].thread_num = 1;
    tcb[1].sum = 0.0;
    pthread_create(&tid1, &attr, func1, &tcb[0]);
    pthread_create(&tid2, &attr, func1, &tcb[1]);
    pthread_join(tid1, NULL);
    pthread_join(tid2, NULL);
    printf("Total from thread 0 = %f\n", tcb[0].sum);
    printf("Total from thread 1 = %f\n", tcb[1].sum);
    printf("Grand total         = %f\n", tcb[0].sum + tcb[1].sum);
    return 0;
}

Существуют и другие способы обработки ввода и вывода из функции потока, но создание простой структуры кажется довольно простым и целесообразным.

С учетом файла данных (input.txt):

 23.192048
  4.128715
  3.465737
 74.307105
  4.329846
  6.098813
  9.497566
  6.988740
 11.530497
 53.262049
  9.469198
 41.305744

Выход за один прогон составил:

Total from thread 0 = 87.377665
Total from thread 1 = 109.423451
Grand total         = 196.801116

Другие прогоны дали разные значения (один другой прогон полностью изменил два результата). Две суммы соответствуют строкам 6-10 и 1-5 файла данных (который имеет 12 строк). Это показывает, что одному потоку удается запланировать и прочитать свою квоту данных, а затем оставить другой поток для чтения следующей квоты данных. Добавление большего количества потоков (используйте циклы и массивы значений идентификаторов потоков) и большее количество данных может показать различную чередующуюся последовательность операций ввода-вывода.

...