2009-12-22 56 views
0

问题是,当我在单个核心上运行下面的代码时,有时它运行正常,有时会出现分段错误。在多核机器上可能会出现这个问题的频率更高。我需要知道我的程序中引入了这种非确定性,我该如何解决它。谢谢。以非确定方式使用pthread时的分段错误

int numThreads = 4; 

class Evaluator; 

struct E { 
    Evaluator* evaluator; 
    int id; 
}; 

class Evaluator { 
public: 
    pthread_t * threads; 
    sem_t* fork_sync; 
    sem_t* join_sync; 
    int tin; 
    pthread_mutex_t tin_mut; 

    double * d; 
    int sz; 
    int cursor; 
    pthread_mutex_t c_mut; 

    Evaluator(sem_t* fs, sem_t* js) { 
     fork_sync = fs; 
     join_sync = js; 
     threads = new pthread_t[numThreads]; 
     tin = 0; 
     pthread_mutex_init(&tin_mut,NULL); 
     for(int i=0 ;i<numThreads; i++) { 
      E arg; 
      arg.evaluator = this; 
      arg.id = i; 
      pthread_create(&threads[i],NULL,(void* (*) (void*))func,(void*)&arg); 
     } 


     //dummy init 
     sz = 20; 
     d = new double[sz]; 
     for(int i=0; i<sz ; i++) d[i] = .5 + i; 
     cursor = 0; 
     pthread_mutex_init(&c_mut,NULL); 
    } 

    static void func(E* e) {   
     Evaluator* eval = e -> evaluator; 
     eval -> go(e -> id); 
    } 

    void reset() { 
     cursor = 0; 
    } 

    void go(int id) { 
     while(1) { 
      sem_wait(fork_sync); 

      pthread_mutex_lock(&tin_mut); 
      ++tin; 
      pthread_mutex_unlock(&tin_mut); 

      while(1) { 
       int idx; 
       pthread_mutex_lock(&c_mut); 
       idx = cursor++; 
       pthread_mutex_unlock(&c_mut); 
       if(idx >= sz) break; 
       // do the evaluation 
       cout << "evaluating index " << idx << " using thread " << id << endl; 
      } 

      int remain; 
      pthread_mutex_lock(&tin_mut); 
      remain = --tin; 
      pthread_mutex_unlock(&tin_mut); 
      if(remain == 0) sem_post(join_sync); 
     } 
    } 


}; 

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

    sem_t fork_sync; 
    sem_t join_sync; 

    sem_init(&fork_sync,0,0); 
    sem_init(&join_sync,0,0); 

    Evaluator e(&fork_sync,&join_sync); 


    //evaluating t times 
    int t = 3; 
    for(int i=0; i<t; i++) { 
     cout << "---------- evaluation number :" << i << endl; 
     e.reset(); 
     for(int j=0; j<numThreads; j++) sem_post(&fork_sync); 
     sem_wait(&join_sync); 
     cout << endl; 
    } 

    return 0; 
} 
+0

go()中最外层while循环的目的是什么?我看到它的方式,它不应该在那里 - 线程应该结束,只有最后一个线程应该发布信号量。 – Thanatos 2009-12-22 07:32:04

+0

的目的是保持线程在需要时等待执行代码。这是因为我不想在每次需要它们执行那段代码时创建线程。 – Navid 2009-12-22 07:45:44

回答

1

这里是一个快速解决第二个错误。这确保工作线程在启动另一个迭代之前完成。

--- a/misc/so/sem_wait/q.cpp 
+++ b/misc/so/sem_wait/q.cpp 
@@ -83,7 +83,7 @@ public: 
      pthread_mutex_lock(&tin_mut); 
      remain = --tin; 
      pthread_mutex_unlock(&tin_mut); 
-   if(remain == 0) sem_post(join_sync); 
+   sem_post(join_sync); 
     } 
    } 

@@ -107,9 +107,11 @@ int main(int argc, char *argv[]) { 
     cout << "---------- evaluation number :" << i << endl; 
     e.reset(); 
     for(int j=0; j<numThreads; j++) sem_post(&fork_sync); 
-  sem_wait(&join_sync); 
+  for(int j=0; j<numThreads; j++) sem_wait(&join_sync); 
     cout << endl; 
    } 

+ cout << "exit" << endl; 
+ 
    return 0; 
} 
+0

谢谢,我用类似的方法修复了它! – Navid 2009-12-22 17:04:59

0

您能否在debugger中重现问题?

如果您编译了优化的代码;尽管问题很可能出现在您的代码中,但可能值得尝试关闭编译器优化(-O0)

+1

线程问题在调试器中非常难以调试,尤其是竞争条件。但它总是值得一试! – 2009-12-22 12:34:02

+0

拉尔斯:我完全意识到这一点,但是由于原始海报没有提到尝试和失败,我认为这可能会导致不好的练习,“在我试图弄清楚这个问题之前让我们问问互联网”:) – Kimvais 2009-12-22 14:39:51

1

Navid,请您提供一个下次可用的示例吗?

它不会伤害那么多添加上你的例子

#include <pthread.h> 
#include <semaphore.h> 
#include <iostream> 

using namespace std; 

// compile with: g++ -g -pthread main.cpp -o main -lrt -lpthread 

顶部以下行当我在调试器中启动该程序,然后确实崩溃有时在sem_wait()线(有时它不会崩溃!)

void go(int id) { 
    while(1) { 
     sem_wait(fork_sync); // <--- seems to crash here 
     ... 
+0

当然,你是对的,这种方式更容易编译,我只是希望它更短:) 我没有看到它应该在这一行崩溃的原因,没有悬挂在那里的指针!顺便说一句,谢谢 – Navid 2009-12-22 07:59:14

+0

不客气!不幸的是,我不是一个pthreads专家。但这是我一直在等待的机会,我一定会“留在线上”,直到这个问题解决:-) – 2009-12-22 08:06:29

2

arg在堆栈上。您正在使用其地址并将此地址传递给另一个线程。竞争条件(堆栈中的值可以在新创建的线程读取之前被覆盖)。

E arg; 
arg.evaluator = this; 
arg.id = i; 
pthread_create(&threads[i],NULL,(void* (*) (void*))func,(void*)&arg); 

解决方案:

E* arg = new E(); 
arg->evaluator = this; 
arg->id = i; 
pthread_create(&threads[i],NULL,(void* (*) (void*))func,(void*)arg); 

而且在func不要忘记delete e

+0

同样的行为,它有时会崩溃,有时它不会 – 2009-12-22 08:29:02

+1

我会争论该行为是不一样的 - 它在另一个地方崩溃:)显然这个代码中有多个错误。信号量使用不正确。在'main()'和'sem_post(join_sync)'的末尾添加print语句变得很明显。在所有线程完成之前,程序退出(并且Evaluator对象被销毁)。任务不按您期望的方式在线程之间分配。 – ygrek 2009-12-22 10:38:43

+0

你是对的,有一个逻辑问题。我想在所有线程完成内部操作后执行sem_post(join_sync),问题在于为此检查了错误的条件,因为在其他线程处于内部之前,锡可能变为1和0。因此sem_post(join_sync)由多于一个线程执行,未被指明的东西。在堆上分配arg(使用新的)后感谢 – Navid 2009-12-22 11:01:18

1

对象的地址被破坏。这是由于在堆栈上分配args元素引起的。当线程启动时,它可能包含或不包含有效值。
这是支持Vokuhila-Oliba的答案,因为fork_sync是线程第一次尝试访问对象内存。
编辑
的代码对我的作品有以下改动(20次测试没有崩溃)


for(int i=0 ;i<numThreads; i++) { 
          E* arg = new E; 
          arg->evaluator = this; 
          arg->id = i; 
          pthread_create(&threads[i],NULL,func,arg); 
         } 

static void* func(void* e) { 
     Evaluator* eval = reinterpret_cast<E*>(e) -> evaluator; 
     eval -> go(reinterpret_cast<E*>(e) -> id); 
     delete(e); 
     return NULL; 
    } 
+0

,问题依然存在。 – Navid 2009-12-22 10:16:12