Безусловно, самый простой метод - использовать один поток для чтения. Однако, учитывая, что это упражнение по использованию потоков 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 строк). Это показывает, что одному потоку удается запланировать и прочитать свою квоту данных, а затем оставить другой поток для чтения следующей квоты данных. Добавление большего количества потоков (используйте циклы и массивы значений идентификаторов потоков) и большее количество данных может показать различную чередующуюся последовательность операций ввода-вывода.