2015-09-06 62 views
0

我在C中有一个多线程程序,只有1个线程函数(thread_read_file),它将从结构中读取文件内容到变量中。 这个函数有一个自动变量thiscontent,其中文件的内容应该使用fread存储。c线程并发和内存混淆

主程序正在执行4个线程以定期读取4个不同的文件将内容更新到内存中。

无论我尝试过什么,我都会在一段时间后得到结果,接着是一些垃圾。 我试着在阅读后强制添加'\ 0',但是不,它会以任何方式显示垃圾。

这里是代码:

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

#define WAIT_TIME 2 

/* change_it: 
* 8: picture for texture 0 finished to load into ram, needs to be set to 2 afterwards 
* 7: picture for texture 0 loading 
* 6: picture for texture 1 finished to load into ram, needs to be set to 2 afterwards 
* 5: picture for texture 1 loading 
* 4: pre stage 1, initialize time variable for time constant blending 
* 3: initial -> allows initial loading of textures 
* 2: texture has finished changed, dont change it anymore 
* 1: initiate texture change 
* 0: don't change any texture */ 
int change_it = 0; 
signed int offset = -1; 
int check_id = WAIT_TIME*100+1; 

typedef struct { 
    pthread_mutex_t read_mutex; // synchronize access to reading flag 
    int reading;        // 0 = not reading , 1 = reading , 2 = finished reading 
    char *content;       // content of file 
    char *file;        // file to read 
} struct_file_content; 

struct_file_content struct_data_file[4]; 

char *file_content[4]; 

// temperature in abu dhabi 
#define file1 "/home/tias/repository/data/temp_ad.txt" 

// temperature in irsch 
#define file2 "/home/tias/repository/data/temp_ir.txt" 

// change EUR/AED 
#define file3 "/home/tias/repository/data/change.txt" 

// monitoring data used to color cubes 
#define file4 "/home/tias/repository/data/summary.txt" 

void change_or_not(signed int* offset, int* check_id, int* change_it) { 

    if (*offset == -1) { 
     *offset = (signed int) (time(NULL)) % WAIT_TIME; 
    } 

    int delta  = (int) ((((long) time(NULL)) - *offset) % WAIT_TIME); 
    int delta_id = (int) ((long) time(NULL) % (WAIT_TIME * 100)); 

    if (*check_id > WAIT_TIME*100) *check_id = delta_id; 

    if (delta == 0 && *check_id != delta_id && *change_it == 0) { 
     //*change_it = 4; 
     *change_it = 1; 
     *check_id = delta_id; 
    } 

    if (delta == 0 && *check_id != delta_id && *change_it == 2) *change_it = 0; 

} 

void pretend_texture_blending (int* change_it) { 
    static old_change_it = 0; 
    static int ori = 0; 
    if (old_change_it == 0 && *change_it == 1) { 
     ori = time(NULL); 
    } 
    if (time(NULL) > ori && *change_it == 1) { 
     ori = time(NULL); 
     *change_it = 2; 
    } 
    old_change_it = *change_it; 
} 

void *thread_read_file(void *data) { 

    struct_file_content *thisdata = data; 
    int mutex_res; 
    long length; 
    char *thiscontent = NULL; 

    // lock mutex 
    mutex_res = pthread_mutex_lock(&thisdata->read_mutex); 

    if (mutex_res != 0) { 
     fprintf(stderr, "thread_read_file() failed to acquire mutex: %s\n", strerror(mutex_res)); 
     pthread_exit(NULL); 
    } else { 
     fprintf(stdout, "thread_read_file: mutex locked for file: %s\n",thisdata->file); 
    } 
    // end locking procedure 

    // let's open the file now 
    FILE *f = fopen(thisdata->file, "r"); 

    // let's get the size of the file and bounce if it is too large 
    // if size is acceptable, read the file into memory 
    if (f) { 
     fseek(f, 0, SEEK_END); 
     length = ftell(f); 
     fseek (f, 0, SEEK_SET); 

     if (length > 39) { 
      fprintf(stderr, "file %s is too big\n", thisdata->file); 
      pthread_exit(NULL); 
     } 

     thiscontent = (char*)malloc((length+1)*sizeof(char)); 

     if (thiscontent) { 
      fread(thiscontent, 1, length, f); 
     } else { 
      perror("malloc(3) error"); 
      pthread_exit(NULL); 
     } 

     fclose(f); 

     // puzzling result is here : 
     // first run with the 4 threads always work fine 
     // next runs will randomly display mixed values/other memory locations 

     fprintf(stdout,"thiscontent: %s length was %d for file %s\n",thiscontent,length,thisdata->file); 

    } else { 
     fprintf(stderr, "cannot open file %s\n", thisdata->file); 
     pthread_exit(NULL); 
    } 
    // file read and fd closed 

    // free old allocation of this content 
    free(thisdata->content); 
    thisdata->reading = 2; 
    // assign new result to newly freed char* 
    thisdata->content = thiscontent; 

    // unlock mutex as everything has been updated 

    mutex_res = pthread_mutex_unlock(&thisdata->read_mutex); 

    if (mutex_res != 0) { 
     fprintf(stderr, "thread_read_file() failed to release mutex: %s\n", strerror(mutex_res)); 
     pthread_exit(NULL); 
    } else { 
     fprintf(stdout,"thread_read_file: mutex unlocked for file: %s\n", thisdata->file); 
    } 
    // end unlock mutex procedure 

    return NULL; 
} 

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

    static int init = 1; 

    int p,q,r,curtime; 
    float X,Y; 
    static int data_update_done[4] = { 0,0,0,0 }; 

    static pthread_t thread_file[4]; 
    static int thread_res[4] = { 0,0,0,0 }; 

    static char *files[4]; 
    files[0] = (char*)malloc(sizeof(char)*strlen(file1)); 
    files[1] = (char*)malloc(sizeof(char)*strlen(file2)); 
    files[2] = (char*)malloc(sizeof(char)*strlen(file3)); 
    files[3] = (char*)malloc(sizeof(char)*strlen(file4)); 

    int mutex_err[4]; 

    // init = 1 
    // => launches the 4 threads for reading the 4 files 
    // 
    if (init == 1) { 

     strcpy(files[0],file1); 
     strcpy(files[1],file2); 
     strcpy(files[2],file3); 
     strcpy(files[3],file4); 

     for (p = 0 ; p < 4 ; p++) { 

      mutex_err[p] = pthread_mutex_init(&struct_data_file[p].read_mutex, NULL); 

      if (mutex_err[p] != 0) { 
       fprintf(stderr, "pthread_mutex_init(3) error: %s\n", strerror(mutex_err[p])); 
       exit(EXIT_FAILURE); 
      } 

      struct_data_file[p].reading = 1; 
      struct_data_file[p].content = NULL; 
      struct_data_file[p].file = files[p]; 

      thread_res[p] = pthread_create(&thread_file[p], NULL, thread_read_file, &struct_data_file[p]); 

     } 

    init = 0; 

    } 

    // main loop 
    while (1) { 

     change_or_not(&offset, &check_id, &change_it); 
     usleep(50000); 
     fprintf(stdout,".%u.",change_it); 
     fflush(stdout); 
     pretend_texture_blending(&change_it); 

     for (p = 0 ; p < 4 ; p++) { // updating data if ready, destroying mutex when done, etc... 

      // lock mutex 
      mutex_err[p] = pthread_mutex_lock(&struct_data_file[p].read_mutex); 

      if (mutex_err[p] != 0) { 
       fprintf(stderr, "pthread_mutex_lock(3) error: %s\n",strerror(mutex_err[p])); 
       exit(EXIT_FAILURE); 
      } 
      // end lock mutex 

      // data update routine 
      // is data ready: check variable 'reading' from the struct_data_file struct 
      // 2   -> yes : update + destroy mutex 
      // 1 or 0 -> no : unlock mutex 

      if (struct_data_file[p].reading == 2) { // data is ready 
       if (file_content[p]) free(file_content[p]); 
       file_content[p] = (char *)malloc(strlen(struct_data_file[p].content)*sizeof(char)); 
       strcpy(file_content[p],struct_data_file[p].content); 
       struct_data_file[p].reading = 0; 
       mutex_err[p] = pthread_mutex_unlock(&struct_data_file[p].read_mutex); 
       if (mutex_err[p] != 0) { 
        fprintf(stderr, "Warning: Error destroying mutex: %s\n", strerror(mutex_err[p])); 
       } 
      } else { 
       mutex_err[p] = pthread_mutex_unlock(&struct_data_file[p].read_mutex); 
       if (mutex_err[p] != 0) { 
        fprintf(stderr, "pthread_mutex_unlock(3) error: %s\n",strerror(mutex_err[p])); 
        exit(EXIT_FAILURE); 
       } 
      } 
      // end checking if data is ready 
     } 

     if (change_it == 1) { 
      for (p = 0 ; p < 4 ; p++) { 
       if (struct_data_file[p].reading == 0) { 
        if (data_update_done[p] == 0) { 
         thread_res[p] = pthread_create(&thread_file[p], NULL, thread_read_file, &struct_data_file[p]); 
         data_update_done[p] = 1; 
        } 
       } 
      } 
     } 

     if (change_it == 2) { 
      for (p = 0 ; p < 4 ; p++) { 
       data_update_done[p] = 0; 
      } 
     } 

    // end main loop 
    } 
// end main 
} 

输出

thiscontent: abu dhabi:35.6 length was 14 for file /home/tias/repository/data/temp_ad.txt 
thread_read_file: mutex unlocked for file: /home/tias/repository/data/temp_ad.txt 
thread_read_file: mutex locked for file: /home/tias/repository/data/summary.txt 
thiscontent: AED/euro:4.09534 length was 16 for file /home/tias/repository/data/change.txt 
thiscontent: 11111 length was 5 for file /home/tias/repository/data/summary.txt 
thread_read_file: mutex unlocked for file: /home/tias/repository/data/summary.txt 
thread_read_file: mutex unlocked for file: /home/tias/repository/data/change.txt 
.1..1..1..1..1..1..1..1..1..1..1..1..1..1..1..1..1..1..2..2..2..2..2..2..2..2..2..2..2..2..2..2..2..2..2..2..2..2..0..1.thread_read_file: mutex locked for file: /home/tias/repository/data/temp_ir.txt 
thread_read_file: mutex locked for file: /home/tias/repository/data/temp_ad.txt 
thread_read_file: mutex locked for file: /home/tias/repository/data/summary.txt 
thread_read_file: mutex locked for file: /home/tias/repository/data/change.txt 
thiscontent: AED/euro:4.09534 length was 16 for file /home/tias/repository/data/change.txt 
thread_read_file: mutex unlocked for file: /home/tias/repository/data/change.txt 
thiscontent: abu dhabi:35.6 length was 14 for file /home/tias/repository/data/temp_ad.txt 
thread_read_file: mutex unlocked for file: /home/tias/repository/data/temp_ad.txt 
thiscontent: irsch:14.6.09534 length was 10 for file /home/tias/repository/data/temp_ir.txt 
thread_read_file: mutex unlocked for file: /home/tias/repository/data/temp_ir.txt 
thiscontent: 1111 length was 5 for file /home/tias/repository/data/summary.txt 
thread_read_file: mutex unlocked for file: /home/tias/repository/data/summary.txt 
.1..1..1..1..1..1..1..1..1..1..1..1..1..1..1..1..1..1..2..2..2..2..2..2..2..2..2..2..2..2..2..2..2..2..2..2..2..2..0..1.thread_read_file: mutex locked for file: /home/tias/repository/data/temp_ad.txt 
thread_read_file: mutex locked for file: /home/tias/repository/data/temp_ir.txt 
thread_read_file: mutex locked for file: /home/tias/repository/data/summary.txt 
thread_read_file: mutex locked for file: /home/tias/repository/data/change.txt 
thiscontent: irsch:14.6.09534 length was 10 for file /home/tias/repository/data/temp_ir.txt 
thread_read_file: mutex unlocked for file: /home/tias/repository/data/temp_ir.txt 
thiscontent: abu dhabi:35.6 length was 14 for file /home/tias/repository/data/temp_ad.txt 
thread_read_file: mutex unlocked for file: /home/tias/repository/data/temp_ad.txt 
thiscontent: AED/euro:4.09534 length was 16 for file /home/tias/repository/data/change.txt 
thread_read_file: mutex unlocked for file: /home/tias/repository/data/change.txt 
thiscontent: 1111 length was 5 for file /home/tias/repository/data/summary.txt 
thread_read_file: mutex unlocked for file: /home/tias/repository/data/summary.txt 

的4个文件具有固定的内容。

前4个结果总是正确的(4个线程读取4个文件,第一组结果总是正确的,据我所见,我已经做了很多测试)。

然而,当显示变量thiscontent(后第一次运行),我得到额外的输出或更少的输出(带有包含11111文件的summary.txt)

例如: 我thiscontent:伊尔施:14.6.09534 我应该得到thiscontent:伊尔施:14.6

而且 我应该得到所有的时间: thiscontent:11111长度为5 但有时我得到 thiscontent:1111长度为5

我不明白为什么,也不能修复它。 我对变量thiscontent是自动的事实感到困惑,因此应该在每次函数调用时都进行初始化。然而,输出显示不同。

它可能是一个内存分配问题,但我目前不知道。

任何帮助非常感谢。 PS:你可能会发现一些无用的代码,这是因为主程序是我编写的一个opengl屏幕保护程序,并且长度为1000行。

我发布的代码是来自此屏幕保护程序的摘录,重现了此问题,但没有大量无用代码用于此非常故障排除。

谢谢

PS NO2:您可以复制/粘贴此代码到一个名为threads.c并使用编译:

gcc -o threads threads.c -lpthread -D_REENTRANT 
+0

'的文件[0] =的malloc(1 + strlen的(文件1));'也:' strdup()'是你的朋友。 – wildplasser

回答

0

要打印的文件内容作为一个字符串,但是你还没有添加一个nul结束符。因此,字符串不会终止 - 您看到的额外输出是发生在内存之外的内存中的垃圾。

如果您有:

 fread(thiscontent, 1, length, f); 

您还需要添加NUL终止:

 thiscontent[length] = 0; 
+0

是的,我做到了,它的工作原理。我不确定我之前是如何做到这一点的,因为那时它不起作用。我添加了一个thiscontent [length] ='\ 0';它工作正常。谢谢 –

+0

然而,还有另一种竞争条件需要考虑:在代码中工作线程中写入新文件比赛的新版本的其他进程:''p2:fseek(...); p1:写入写入p2:读取(长度)''。或者,也许写作过程独占锁定文件,这将不得不考虑。 – BitTickler