MPI程序中的多进程写冲突问题的解决与遗留问题

  最近碰到关于多进程写文件的问题,问题的描述如下:有一个MPI程序,会以n个进程运行程序,然后在每个进程结束的时候向timer.txt文件写入数据。

  开始的时候,用的是普通的写文件操作,显然不行,若是各个进程结束的时间相差较大,不会有错误,若是执行时间相差很小,大约是几是个毫秒,就会导致出错,其实这种做法本身就存在问题。

  后来,参考unix环境高级编程提到的方法,又试用了其中三种方法,最后还是出错。由于之前未接触多进程编程,所以对错误的原因不是很确定,不知道是否为自己用法有问题,特写出来大家讨论下。

  方法一:用open(time.txt, O_APPEND |  O_WRONLY ),具体代码如下:

 

 int w_timer_handle;
 char temp[1000];
 int  len;
 int w_len;
 int offset;
sprintf(temp, "process %d time_total %lf comm_time %lf/n",llrank, total_time_last[llrank],  comm_time[llrank] );
 len = strlen(temp);
 w_timer_handle = open( ftimer, O_WRONLY | O_APPEND );
  w_len = write( w_timer_handle, temp, len ); 
  fsync( w_timer_handle );
   if( w_len != len )
        printf("write to timer file error. /n");
    printf("The length is: %d , and the write length is: %d /n", len, w_len );
    close( w_timer_handle );

  用该方法仍然无法避免丢失某些数据,就是有些进程并未将数据写出。

 

方法二:用PWRITE()原子操作

  int w_timer_handle;
  char temp[1000];
    int  len;
  int w_len;
    int offset;
   
   
    offset = llrank * 60;
    printf("%d  offset: %d /n", llrank, offset );
  sprintf(temp, "process %d time_total %lf comm_time %lf/n",llrank, total_time_last[llrank], comm_time[llrank] );
    len = strlen(temp);
    w_timer_handle = open( ftimer, O_WRONLY | O_APPEND );
     w_len = write( w_timer_handle, temp, len );
    //w_len = pwrite( w_timer_handle, temp, len, offset);
    fsync( w_timer_handle );
    if( w_len != len )
        printf("write to timer file error. /n");
    printf("The length is: %d , and the write length is: %d /n", len, w_len );
    close( w_timer_handle );

 

方法三: 用记录锁的方法,该方法使的丢失数据最少,但是也没有达到要求,理论上觉得该方法应该没有问题的。

   struct flock timer_flock;
    //int fd;
   

    //fd = open( ftimer, O_APPEND | O_WRONLY );
   
    timer_flock.l_type = F_WRLCK;
    timer_flock.l_start = 0;
    timer_flock.l_whence = SEEK_SET;
    timer_flock.l_len = 0;

    while ( fcntl( fd, F_SETLK, &timer_flock ) < 0 )
    {
        printf("process %d Can't set_lock /n", llrank );
        sleep( llrank+1 );
    }

     w_len = write( fd, temp, len );
     fsync( fd );
     if( w_len != len )
          printf("write to timer file error. /n");
     close( fd );

  说明,开始得时候,把fd作为局部变量,发现仍然存在以前的问题,又改为全局变量还是不ok;后来分析下觉得其实跟全局变量或者局部变量没有关系。

 

  方法四:利用标准输入输出,但是fseek进行重定位,这个方法理论上觉得完全可行,但不知道什么原因,总是输出一些乱码,也就是有效数据给写成乱码的形式,不知道是什么原因。

 
 
    char temp[1000];
    int len;
    int w_len;
    int offset;

    sprintf(temp, "process %d time_total %lf comm_time %lf",llrank, total_time_last[llrank], comm_time[llrank] );
    len = strlen( temp );
    for( i = len; i< 59; i++ )
        sprintf( temp+i, " ");
    sprintf( temp+59, "/n" );
    pf_timer = fopen( ftimer, "a+" );
  rewind( pf_timer );
    offset = 60 * llrank;
  fseek( pf_timer, offset, SEEK_SET );
      err = fprintf( pf_timer, "%s", temp );
    fflush( pf_timer );
    if( err < 0 )
        printf("write to timer error /n"); 
    fclose( pf_timer );

 

最后,虽然采用另一种方法,就是只让一个进程进行文件写操作,从其他进程收集将要输出的数据,然后该进程一直等待,当所有进程都获得数据后,再有0号进程,进行写操作。

 

 

   我对前面的几个方法进行了简单分析,不知道是否正确。例如O_APPEND虽然是原子操作,但是由于MPI程序中几个进程独立运行,而理论上O_APPEND之所以有效,是因为进程A B在被调度时同时完成fseek与open,但是若几个进程在不同的节点上(相当于几台不同机器,用网络连接起来,但是共用一个磁盘系统),不受统一调度控制,所以仍然无法保证写冲突,另外几个方法,我觉得原因可能也是如此。

 

  请有兴趣的人共同分析。

你可能感兴趣的:(MPI编程,C,C++,.NET)