【问题标题】:global static boolean pointer causes segmentation fault using pthread全局静态布尔指针使用 pthread 导致分段错误
【发布时间】:2010-06-06 01:19:16
【问题描述】:

pthread 编程新手,在处理 C++&C 混合代码时遇到此错误。

我所做的是在c++代码创建的线程中调用c代码。线程中使用了一个静态布尔指针is_center,并且应该在线程完成时释放。

但是我注意到每次程序处理到c函数时,布尔指针的值都会改变,然后由于free()而发生分段错误。并且仅在使用 c 代码时才会出现问题。去掉c代码,多线程c++部分运行良好。

详细代码如下:

static bool *is_center;

// omit other codes in between ...

void streamCluster( PStream* stream)
{
    // some code here ...
    while(1){
        // some code here ...
        is_center = (bool*)calloc(points.num,sizeof(bool));

        // start the parallel thread here.
        // the c code is invoked in this function.
        localSearch(&points,kmin, kmax,&kfinal); // parallel

        free(is_center);
    }

而使用parallel的函数如下(我的c代码在每个线程中调用):

void localSearch( Points* points, long kmin, long kmax, long* kfinal ) {
    pthread_barrier_t barrier;
    pthread_t* threads = new pthread_t[nproc];
    pkmedian_arg_t* arg = new pkmedian_arg_t[nproc];

    pthread_barrier_init(&barrier,NULL,nproc);

    for( int i = 0; i < nproc; i++ ) {
            arg[i].points = points;
            arg[i].kmin = kmin;
            arg[i].kmax = kmax;
            arg[i].pid = i;
            arg[i].kfinal = kfinal;
            arg[i].barrier = &barrier;

            pthread_create(threads+i,NULL,localSearchSub,(void*)&arg[i]);
    }

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

    delete[] threads;
    delete[] arg;
    pthread_barrier_destroy(&barrier);
}

终于调用我的c代码的函数了:

void* localSearchSub(void* arg_) {                                                                                                                                                        

    int eventSet = PAPI_NULL;                                                                                                                                                                                                                                                                                                                                     
    begin_papi_thread(&eventSet);                                                                                                                                                         

    pkmedian_arg_t* arg= (pkmedian_arg_t*)arg_;                                                                                                                                             
    pkmedian(arg->points,arg->kmin,arg->kmax,arg->kfinal,arg->pid,arg->barrier);                                                                                                            

    end_papi_thread(&eventSet);                                                                                                                                                                                                                                                                                                                                                 

    return NULL;                                                                                                                                                                            
}   

从 gdb 获得的 is_center 是:

Breakpoint 2, localSearchSub (arg_=0x600000000000bc40) at streamcluster.cpp:1711
1711      end_papi_thread(&eventSet);
(gdb) s

Hardware watchpoint 1: is_center

Old value = (bool *) 0x600000000000bba0
New value = (bool *) 0xa93f3
0x400000000000d8d1 in localSearchSub (arg_=0x600000000000bc40) at streamcluster.cpp:1711
1711      end_papi_thread(&eventSet);

有什么建议吗?提前致谢!

关于代码的一些新信息:对于 c 代码,我使用的是 PAPI 包。我编写了自己的 papi 包装器来初始化和读取系统计数器。代码如下:

void begin_papi_thread(int* eventSet)                                                                                                                                                     
{                                                                                                                                                                                         
    int thread_id = pthread_self();                                                                                                                                                       
    // Events                                                                                                                                                                             
    if (PAPI_create_eventset(eventSet)) {                                                                                                                                                 
        PAPI_perror(return_value, error_string, PAPI_MAX_STR_LEN);                                                                                                                        
        printf("*** ERROR *** Failed to create event set for thread %d: %s\n.", thread_id, error_string);                                                                                 
    }                                                                                                                                                                                     
    if((return_value = PAPI_add_events(*eventSet, event_code, event_num)) != PAPI_OK)                                                                                                     
    {                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                        
        printf("*** ERROR *** Failed to add event for thread %d: %d.\n", thread_id, return_value);                                                                                                                                                                                                                                                                         
    }                                                                                                                                                                                     
    // Start counting                                                                                                                                                                     
    if ((return_value = PAPI_start(*eventSet)) != PAPI_OK) {                                                                                                                                                                                                                                                                                                              
        PAPI_perror(return_value, error_string, PAPI_MAX_STR_LEN);                                                                                                                                                                                                                                                                                                         
        printf("*** ERROR *** PAPI failed to start the event for thread %d: %s.\n", thread_id, error_string);
    }                                                                                                                                                                                                                                                                                                                                                                             
}  
void end_papi_thread(int* eventSet)                                                                                                                                                       
{                                                                                                                                                                                         
    int thread_id = pthread_self();                                                                                                                                                       
    int i;                                                                                                                                                                                

    long long * count_values = (long long*)malloc(sizeof(long long) * event_num);                                                                                                         
    if (PAPI_read(*eventSet, count_values) != PAPI_OK)                                                                                                                                    
        printf("*** ERROR *** Failed to load count values.\n");                                                                                                                           

    if (PAPI_stop(*eventSet, &dummy_values) != PAPI_OK) {
        PAPI_perror(return_value, error_string, PAPI_MAX_STR_LEN);
        printf("*** ERROR *** PAPI failed to stop the event for thread %d: %s.\n", thread_id, error_string);
        return;
    }
    if(PAPI_cleanup_eventset(*eventSet) != PAPI_OK)
        printf("*** ERROR *** Clean up failed for the thread %d.\n", thread_id);                                                                                                          
} 

【问题讨论】:

  • 没有足够的代码。 localSearchSub() 中有什么内容?不过,您在观察点的正确轨道上。仔细查看streamcluster.cpp:1711 周围的代码,检查数组是否过度/不足,任何指针操作。尝试将所有内容简化到所有步骤都清晰可见的程度。
  • 问题出现在我使用的c代码上。我使用 PAPI api 来捕获每个线程中的硬件计数器,所以我编写了一个如上所述的包装器来初始化它并开始捕获。我在调试的时候发现,在调用end_papi_thread之后,is_center的地址发生了变化,然后free(is_center)触发了segmentation fault。删除我的 papi 包装代码后,一切正常。所以我对地址的更新感到非常困惑。 free(is_center) 在我看来应该在主线程的上下文中工作,但它似乎在线程上下文中......

标签: c++ global-variables pthreads papi


【解决方案1】:

我认为您发布的代码不足以真正理解您的问题,但您声明 is_center 全局看起来很可疑。我假设你在不止一个地方使用它,可能是多个线程(localSearchSub 提到它,这是你的工作线程函数)。

如果is_center 正被多个线程读取或写入,您可能希望使用pthread mutex 保护它。您说它“在线程完成时被释放”,但您应该知道有 nprocs 线程,看起来它们都在处理 is_center[points] 布尔数组。如果points != nproc,这可能是一件坏事[1]。每个线程可能应该在自己的数组上工作,localSearch 应该聚合结果。

xxx_papi_thread 函数在 Google 上没有得到任何点击,所以我只能想象它是您自己的……如果问题在那里,我们不太可能为您提供帮助 :)

[1]:即使points == nproc,也不一定可以从多个线程写入数组的不同元素(它取决于编译器和处理器)。安全起见,请使用互斥锁。

另外,这被标记为C++。你能用vectors 替换calloc 和动态数组(使用new)吗?它最终可能更容易调试,而且它肯定更容易维护。你为什么讨厌并想惩罚你的代码的读者? ;)

【讨论】:

  • 嗨斯蒂芬,感谢您的建议!我已经发布了更多代码,也许你可以再看看它们?目前我认为问题应该出在is_center的全球共享上。我认为我的c代码对它没有任何影响,对吧?可能来自多线程?
  • 我猜是的,虽然你添加的代码似乎没有使用它......所以,我不知道 :) papi_thread 包装器没有任何明显的错误(但我不熟悉 PAPI 库)。但是,我在 Google 代码上找到了 pkmedian 的代码。编译时是否定义ENABLE_THREADS?看来有必要使用屏障了:code.google.com/p/ua-gpu/source/browse/trunk/rodinia/openmp/…
  • 是的,线程已启用。如果我没有误解,pthread_barrier 应该可以隔离写冲突吗?顺便说一句,该错误仅在我使用 PAPI 包装器时发生(多线程在没有包装器的情况下运行良好)......也许包装器仍然有问题?
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2021-05-25
  • 2021-10-02
  • 1970-01-01
  • 1970-01-01
  • 2018-10-19
  • 2021-03-02
  • 1970-01-01
相关资源
最近更新 更多