TBB管道输出不正确

时间:2014-04-29 05:37:08

标签: c++ multithreading parallel-processing tbb

我在文本文件中编写了一个具有不同值(100次)的C结构,例如 1.txt,2.txt ... 100.txt


我在Linux上使用英特尔TBB。我创建了:

  1. InputFilter(serial_in_order MODE)
  2. TransformFIlter(serial_in_order MODE)
  3. OutputFilter(Serial_in_order MODE)
  4. InputFilter从文件中读取结构并将其传递给TransformFilter。 TrasnformFilter更新结构值并将其传递给OutputFilter。 OutputFilter在光盘上写入新结构。

    基本上,它是一个简单的结构读写应用程序。

    class InputFilter: public tbb::filter {
    public:
        InputFilter( int );
        ~InputFilter();
    private:
        int total_streams;
        int count;
        struct video_process_object input_obj;
        void* operator()( void* );
    };
    
    InputFilter::InputFilter( int x )
            : filter( serial_in_order ) {
        total_streams = x;
        count = 1;
    }
    
    InputFilter::~InputFilter() {
        total_streams = 0;
    }
    
    void* InputFilter::operator()( void* ) {
        char path[50] = { };
        sprintf( path, "input//%d.txt", count );
        printf( "Path : %s\n", path );
        FILE *fp;
        fp = fopen( path, "r" );
    
        if( fp == NULL || count > total_streams ) {
            fclose( fp );
            printf( "\n*******Cannot find more data.Terminating********\n\n\n" );
            return NULL;
        }
    
        fscanf( fp, "%d", &input_obj.video_id );
        fscanf( fp, "%s", &input_obj.storage_url );
        fscanf( fp, "%s", &input_obj.storage_type );
        fscanf( fp, "%d", &input_obj.face_detect );
        fscanf( fp, "%d", &input_obj.face_recognise );
        fscanf( fp, "%d", &input_obj.scene_recognise );
        fscanf( fp, "%d", &input_obj.activity_recognise );
        fscanf( fp, "%d", &input_obj.speech_recognise );
        fclose( fp );
    
        count++;
        return &input_obj;
    }
    
    class TransformFilter: public tbb::filter {
    public:
        TransformFilter();
        ~TransformFilter();
    private:
        struct video_process_object input_transform;
        void* operator()( void* );
    };
    
    TransformFilter::TransformFilter()
            : filter( serial_in_order ) {
    }
    
    TransformFilter::~TransformFilter() {
    }
    
    void* TransformFilter::operator()( void *item ) {
    
        input_transform = *static_cast<struct video_process_object*>( item );
    
        input_transform.video_id += 1000;
        strcat( input_transform.storage_url, "  nabeel" );
        strcat( input_transform.storage_type, " N" );
        input_transform.face_detect += 1000;
        input_transform.face_recognise += 1000;
    
        return &input_transform;
    }
    
    class OutputFilter: public tbb::filter {
    public:
        OutputFilter();
        ~OutputFilter();
    private:
        struct video_process_object output_obj;
        void* operator()( void* );
    };
    
    OutputFilter::OutputFilter()
            : filter( serial_in_order ) {
        int status = mkdir( "output", S_IRWXU | S_IRWXG | S_IRWXO );
        if( status == -1 )
            printf( "\nOutput directory already exists\n\n" );
    }
    
    OutputFilter::~OutputFilter() {
    }
    
    void* OutputFilter::operator()( void *item ) {
    
        output_obj = *static_cast<struct video_process_object*>( item );
    
        FILE *fp;
    
        char path[50] = { };
        sprintf( path, "output//%d.txt", output_obj.video_id - 1000 );
        printf( "Output Path : %s\t\t %d\n\n", path, output_obj.video_id );
    
        if( (fp = fopen( path, "w" )) == NULL ) {
            fprintf( stderr, "Cannot open output file.\n" );
            return NULL;
        }
    
        fprintf( fp, "%d\n", output_obj.video_id );
        fprintf( fp, "%s\n", output_obj.storage_url );
        fprintf( fp, "%s\n", output_obj.storage_type );
        fprintf( fp, "%d\n", output_obj.face_detect );
        fprintf( fp, "%d\n", output_obj.face_recognise );
        fprintf( fp, "%d\n", output_obj.scene_recognise );
        fprintf( fp, "%d\n", output_obj.activity_recognise );
        fprintf( fp, "%d\n", output_obj.speech_recognise );
    
        fclose( fp );
        return NULL;
    }
    
    int main() {
        tbb::pipeline pipeline;
    
        InputFilter input_filter( 100 );
        pipeline.add_filter( input_filter );
    
        TransformFilter transform_filter;
        pipeline.add_filter( transform_filter );
    
        OutputFilter output_filter;
        pipeline.add_filter( output_filter );
    
        tbb::tick_count t0 = tbb::tick_count::now();
    
        tbb::task_scheduler_init init_parallel;
        pipeline.run( 1 );
        tbb::tick_count t1 = tbb::tick_count::now();
    
        return 0;
    }
    

    使用少量文件(如5或10)时,一切正常。当我读取大量文件(如50或100)时,问题就出现了。问题是:

      

    有时InputFilter读取10.txt文件,TransformFilter处理它。但是立即InputFilter读取11.txt。 OutputFIlter跳过10.txt并处理11.txt。

    我如何确保不会发生这种情况?

1 个答案:

答案 0 :(得分:1)

存在数据争用,因为video_process_objects放在过滤器结构中并通过过滤器之间的引用传递(当然并行运行)。因此,当InputFilter开始处理将新数据读入其video_process_object的下一个令牌,而第一个令牌刚开始通过TransformFilter中的相同地址读取数据时,您会遇到这种情况:

     Token 1                ||         Token 2
input_filter.operator()     ||
transform_filter.operator() ||  input_filter.operator()
...

要修复它,请动态分配数据,例如:

struct video_process_object *input_obj_ptr = new video_process_object;
fscanf( fp, "%d", &input_obj_ptr->video_id );
...
return input_obj_ptr;

并在最后一个过滤器中取消分配它,因为无论如何都会忽略它的返回值。 this old presentation中的幻灯片49-50草绘了类似的代码。

最后,让我挑战你选择的tbb :: pipelene和serial_in_order过滤器类型。 TBB Reference说:

  

并行滤波器在实用时是首选,因为它们允许并行加速。如果过滤器必须是串行的,那么在实际使用时会优先使用乱序变量,因为它会减少对处理顺序的约束。

由于处理和文件是独立的,我认为没有理由将此额外的“按顺序”限制。为了结构更好的代码,需要考虑另一个引用:

  

函数parallel_pipeline提供了一种强类型的lambda友好方式来构建和运行管道。