Почему fprintf в моем коде не работает должным образом?

У меня есть функция, которая вычисляет некоторые оценки и распечатывает их в файл. Эта программа вызывается в нескольких потоках для выполнения вычислений для различных начальных условий. Каждый поток создает отдельные файлы для записи значений, чтобы не возникало конфликта файлов. Значения печатаются в экспоненциальном формате. Моя проблема заключается в том, что во время печати, хотя большинство значений печатаются правильно, некоторые (одно или два) печатаются без буквы «e» в экспоненциальной части, имеют некоторые знаки -ve или вообще нарушают форматирование. Хотя это не кажется серьезным, оно нарушает остальную часть конвейера, поскольку программы, использующие эти файлы в качестве входных данных, не могут правильно их проанализировать. Базовый код приведен ниже. Цикл, вызывающий функцию.

Цикл, вызывающий функцию.

for (int i = 0; i < 12; i++) {
        void* args[2] = {clock_pairs[i], Q_matrices[i]};
        pthread_create(&K_threads[i], NULL, calculate_kalman, (void*)args);
        printf("thread no %d ran \n",i);
        
    }

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

При этом начальные условия передаются в функцию, которая объединяет аргументы и передается в функцию, которая вычисляет значения.

void Calculate_Kalman(const char *filename, const char *filename1, double** processNoise)
{
    char kalman_filename[256];
    sprintf(kalman_filename, "K_%s_%s", filename, filename1);
    FILE *kalman_file = fopen(kalman_filename, "w");
        if (!kalman_file)
        {
            perror("Error opening file for writing");
            exit(EXIT_FAILURE);
        }
    char Error_filename[256];
    sprintf(Error_filename, "E_%s_%s", filename, filename1);
    FILE *E_file = fopen(Error_filename, "w");
        if (!E_file)
        {
            perror("Error opening file for writing");
            exit(EXIT_FAILURE);
        }
    
    for (int i = 0; i < SampleSize; i++)
    {

      
        kf.z = diff[i] + normalRandom(0,pow(measurementNoise,0.5));
        
        Kalman(&kf);  // Kalman function call.

        // Print the current state estimate

        fprintf(kalman_file, "%e, %e\n", gsl_matrix_get(kf.x, 0, 0), gsl_matrix_get(kf.x, 1, 0));
        fprintf(E_file, "%e,%e\n", gsl_matrix_get(kf.S, 0, 0), gsl_matrix_get(kf.S, 1, 0));

        // Add kf.S values to the dynamic matrix

        double row[NUM_VARS] = {gsl_matrix_get(kf.S, 0, 0), gsl_matrix_get(kf.S, 1, 0)};
        addRow(&matrix, row);

       
    }
    fclose(kalman_file);}

Я удалил коды, инициализирующие систему, поскольку это не имеет отношения к вопросу. Функция Калмана принимает структуру {kf} и обновляет в ней значения. Команды fprintf в цикле должны печатать строки в файлы. Структура kf определяется как

typedef struct
{
    gsl_matrix *x;    // State estimate (3x1 matrix)
    gsl_matrix *P;    // Error covariance (3x3 matrix)
    gsl_matrix *Q;    // Process noise covariance (3x3 matrix)
    double R;         // Measurement noise covariance (scalar)
    gsl_matrix *K;    // Kalman gain (3x1 matrix)
    gsl_matrix *Phi;  // State transition matrix (3x3 matrix)
    gsl_matrix *S;    // Noise vector (3x1 matrix)
    gsl_matrix *H;
    double z;
} KalmanFilter;

Я пробовал использовать разные стили форматирования (le вместо e), но это ничего не изменило. Тот факт, что эти ошибки появляются совершенно случайно в совершенно случайных файлах, также не помогает.

Обычный вывод выглядит так

-3.257449e-15, -1.437916e-18
8.742356e-11, 5.662028e-14
-1.929949e-10, -1.779626e-13
1.927338e-10, 2.305067e-13
-8.744399e-11, -1.284666e-13
........................
........................

Но некоторые из плохих выходных значений выглядят так

-3.0.049398e-13
1.-12
- -1.922051e-13

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

Что такое calculate_kalman? Потенциально вы передаете одни и те же параметры (void* args[2] ...) всем вызовам этой функции.

G.M. 06.03.2024 12:16
pthread_create(&K_threads[i], NULL, calculate_kalman, (void*)args); Это вызывает неопределенное поведение. args перестанет существовать, когда вы покинете этот цикл for. Вы передаете адрес этого массива только своим потокам, но после этого память больше не действительна. И если случится так, что некоторые потоки будут выполнены до того, как вы выйдете из цикла, все они увидят одни и те же значения.
Gerhardh 06.03.2024 12:16

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

Some programmer dude 06.03.2024 12:18

Что такое calculate_kalman? Связано ли Calculate_Kalman? Я считаю, что смешивать верхний и нижний регистр — плохая идея.

KamilCuk 06.03.2024 12:26
Стоит ли изучать PHP в 2023-2024 годах?
Стоит ли изучать PHP в 2023-2024 годах?
Привет всем, сегодня я хочу высказать свои соображения по поводу вопроса, который я уже много раз получал в своем сообществе: "Стоит ли изучать PHP в...
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Приемы CSS-макетирования - floats и Flexbox
Приемы CSS-макетирования - floats и Flexbox
Здравствуйте, друзья-студенты! Готовы совершенствовать свои навыки веб-дизайна? Сегодня в нашем путешествии мы рассмотрим приемы CSS-верстки - в...
Тестирование функциональных ngrx-эффектов в Angular 16 с помощью Jest
В системе управления состояниями ngrx, совместимой с Angular 16, появились функциональные эффекты. Это здорово и делает код определенно легче для...
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
0
4
55
1
Перейти к ответу Данный вопрос помечен как решенный

Ответы 1

Ответ принят как подходящий

Вам необходимо освежить свои знания об области действия и времени жизни переменных.

for (int i = 0; i < 12; i++) {
        void* args[2] = {clock_pairs[i], Q_matrices[i]};
        pthread_create(&K_threads[i], NULL, calculate_kalman, (void*)args);
        printf("thread no %d ran \n",i);
        
    }

В этом коде у вас есть только один массив args в любой момент времени, и после выхода из цикла переменная вообще больше не действительна. Это означает, что если один из потоков выполняется, пока вы все еще находитесь в цикле (что не гарантировано), он увидит только то, что было сохранено там последним. Если поток выполняется после выхода из цикла, память больше недействительна, и чтение содержимого вызывает неопределенное поведение.

Вы должны использовать статическую память. Либо определите переменную со статическим временем жизни, либо используйте динамическое распределение памяти:

void* args[12][2];
for (int i = 0; i < 12; i++) {
        args[i][0] = clock_pairs[i];
        args[i][1] = Q_matrices[i];
        pthread_create(&K_threads[i], NULL, calculate_kalman, (void*)args[i]);
        printf("thread no %d ran \n",i);
    }

Возможно, ваша распечатка уже показывает, в чем вы ошибаетесь:

printf("thread no %d ran \n",i);

Когда вы достигнете этой строки, поток будет создан и может быть выполнен. Это вовсе не означает, что он уже получил какой-либо цикл процессора или даже выполнил свою задачу.

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

John Bollinger 06.03.2024 23:32

Другие вопросы по теме