2011-03-15 3 views
9

스레드가 아닌 모드에서 정상적으로 작동하는 GCC C 응용 프로그램에서 스레드를 사용하려고합니다 (처음으로!). 내가 그것을 실행할 때 일부 스레드는 필요한 답 (모든 검사 목적으로 알고 있음) 대신 모두 0 인 결과를 제공하지만, 0을주는 스레드는 실행할 때마다 동일하지 않습니다. 0이 아닌 답을주는 코드는 정확하므로 코드가 정상적으로 실행됩니다. 누군가가 스레드로부터 안전하지 않은 부분을 지적 할 수 있는지 궁금합니다.스레드는 잠금없이 동일한 구조체 배열의 다른 요소에 쓸 수 있습니까?

내 생각에 결과 나 메모리 할당을 어떻게 모으는가 때문일 수 있습니다 - StackOverflow에서 malloc과 free를 사용하지만 -lpthread와 연결된 경우 GCC malloc은 thread-safe로 간주됩니다. 하기). 전역/정적 변수를 사용하는 것은 없으며 모든 것이 함수 인수로 전달됩니다.

결과를 main으로 전달하기 위해 스레드 된 루틴은 구조체 배열을 사용합니다. 각 스레드는이 배열의 고유 한 요소에 쓰므로 동일한 메모리에 쓰려고하지 않습니다. 어쩌면 구조 배열의 같은 요소로 이동하지 않아도 결과를 작성할 때 어떤 형태의 잠금을 사용해야합니까?

는 여기 스레드 코드에 대한 레시피를 따라 : https://computing.llnl.gov/tutorials/pthreads/#Abstract

I 부착이 어떤 단서를 제공하는 경우에 (간체) 코드 추출물 (I 잘못/수정 뭔가 생략 한 수 있지만 사람이 발견하는 나는 요구 하진 않았어 버그, 그냥 일반적인 방법론). 이 글을 읽는 다른 사람의 이익을 위해

typedef struct p_struct { /* used for communicating results back to main */ 
    int given[CELLS]; 
    int type; 
    int status; 
    /*... etc */ 
} puzstru; 

typedef struct params_struct { /* used for calling generate function using threads */ 
    long seed; 
    char *text; 
    puzzle *puzzp; 
    bool unique; 
    int required; 
} paramstru; 
/* ========================================================================================== */ 
void *myfunc(void *spv) /* calling routine for use by threads */ 
{ 
    paramstru *sp=(paramstru *)spv; 
    generate(sp->seed, sp->text, sp->puzzp, sp->unique, sp->required); 
    pthread_exit((void*) spv); 
} 
/* ========================================================================================== */ 
int generate(long seed, char *text, puzstru *puzzp, bool unique, int required) 
{ 
/* working code , also uses malloc and free, 
    puts results in the element of a structure array pointed to by "puzzp", 
    which is different for each thread 
    (see calling routine below :  params->puzzp=puz+thr;) 
    extract as follows: */ 
      puzzp->given[ix]=calcgiven[ix]; 
      puzzp->type=1; 
      puzzp->status=1; 
      /* ... etc */ 
} 
/* ========================================================================================== */ 


int main(int argc, char* argv[]) 
{ 
    pthread_t thread[NUM_THREADS]; 
    pthread_attr_t threadattr; 
    int thr,threadretcode; 
    void *threadstatus; 
    paramstru params[1]; 

    /* ....... ETC */ 

/* set up params structure for function calling parameters */ 
    params->text=mytext; 
    params->unique=TRUE; 
    params->required=1; 

    /* Initialize and set thread detached attribute */ 
    pthread_attr_init(&threadattr); 
    pthread_attr_setdetachstate(&threadattr, PTHREAD_CREATE_JOINABLE); 

    for(thr=0; thr<NUM_THREADS; thr++) 
    { 
     printf("Main: creating thread %d\n", thr); 
     params->seed=ran_arr_next(startingseeds); 
     params->puzzp=puz+thr; 
     threadretcode = pthread_create(&thread[thr], &threadattr, myfunc, (void *)params); 
     if (threadretcode) 
     { 
      printf("ERROR; return code from pthread_create() is %d\n", threadretcode); 
      exit(-1); 
     } 
    } 

    /* Free thread attribute and wait for the other threads */ 
    pthread_attr_destroy(&threadattr); 
    for(thr=0; thr<NUM_THREADS; thr++) 
    { 
     threadretcode = pthread_join(thread[thr], &threadstatus); 
     if (threadretcode) 
     { 
      printf("ERROR; return code from pthread_join() is %d\n", threadretcode); 
      exit(-1); 
     } 
     printf("Main: completed join with thread %d having a status of %ld\n",thr,(long)threadstatus); 
    } 

/* non-threaded code, print results etc ............. */ 

    free(startingseeds); 
    free(puz); 
    printf("Main: program completed. Exiting.\n"); 
    pthread_exit(NULL); 
} 

- 모든 답이 정확하고, 제목의 질문에 대한 대답은 YES, 스레드 구조의 동일한 배열의 다른 요소에 안전하게 쓸 수있다, 내 문제였다 호출 루틴에 - 다음은 수정 된 코드입니다 (지금은 잘 작동) :

paramstru params[NUM_THREADS]; 

    for(thr=0; thr<NUM_THREADS; thr++) 
    { 
     printf("Main: creating thread %d\n", thr); 
    /* set up params structure for function calling parameters */ 
     params[thr].text=mytext; 
     params[thr].unique=TRUE; 
     params[thr].required=1; 
     params[thr].seed=ran_arr_next(startingseeds); 
     params[thr].puzzp=puz+thr; 
     threadretcode = pthread_create(&thread[thr], &threadattr, myfunc, (void *)&params[thr]); 
     if (threadretcode) 
     { 
      printf("ERROR; return code from pthread_create() is %d\n", threadretcode); 
      exit(-1); 
     } 
    } 
+0

흠. puz 변수는 어디에 선언 되었습니까? 그것은 '퍼즐 *'유형입니까? 'ix'는 어떻게 계산됩니까? 문제가 "작업 코드"로 표시된 블록에 있다고 생각합니다. ;) –

답변

3
paramstru params[1]; 

코드는 모든 스레드에 동일한 구조를 전달합니다. 그냥 스레드 초기화 루프는 스레드가 작동해야하는 데이터를 덮어 쓰는 경우 : params 구조가 변경되기 전에 myfunc()에 대한 각 호출이 종료하기 때문에 비 스레드 코드가 작동

for(thr=0; thr<NUM_THREADS; thr++) 
    { 
     printf("Main: creating thread %d\n", thr); 
     params->seed=ran_arr_next(startingseeds); /* OVERWRITE */ 
     params->puzzp=puz+thr; /* OVERWRITE */ 

이유입니다.

+1

감사합니다. params를 읽는 스레드와 설정 루프의 다음 반복 사이에 경쟁이 있었다고 생각합니다. – RussellG

+0

이 경주는 장애물로 완만하게 (약간 완만하게) 또는 스레드별로 별도의 구조체를 사용하여 약간 경미하게 조정할 수 있습니다 (약간의 메모리 낭비). –

1

매개 변수 구조의 복사본을 하나만 작성하여 덮어 쓰고 각 스레드에 동일한 주소를 전달했습니다. 너는 paramstru params[NUM_THREADS];을 원하지 않니?

+0

네, 고마워, 내가 할거야. – RussellG

6

질문에 대답하기 위해 잠금없이 다른 스레드에서 동일한 배열의 다른 요소에 쓸 수 있습니다. 두 개의 스레드가 동기화 (예 : 잠금)없이 동일한 바이트에 쓰는 경우에만 data race이됩니다.

다른 답변에서 지적한 것처럼 코드가 작성된 이유는 각 스레드에 동일한 params 개체에 대한 포인터를 전달했기 때문입니다. 그런 다음 해당 개체를 수정합니다. 각 스레드에 대해 새 param을 새로 만들려는 경우 일 수 있습니다.

+3

신중하게하지 않으면 안전하지 만 성능이 좋지 않을 수 있습니다. 여러 스레드가 동일한 캐시 라인에서 배열의 요소에 계속 액세스하는 경우 캐시 라인 바운싱이 많이 발생하므로 비용이 많이 듭니다. – ninjalj

+0

다른 바이트에 관한 정보를 보내 주셔서 감사합니다. 나는 내 문제에 대한 잘못된 곳을 찾고 있었다. – RussellG

+0

안전한 것으로 확신합니까? 바이트 쓰기가 읽기 - 수정 - 쓰기 작업 인 워드보다 작은 쓰기를 수행 할 수없는 시스템은 어떻게됩니까? 나는 그러한 기계가 현실 세계의 목적으로 사용되어서는 안되는 병리학적인 허튼 소리가 아니라 엄밀히 말하면 나는 완전한 휴대 성을 주장 할 때 고려해야한다고 생각한다. –

관련 문제