2016-07-03 3 views
2

멀티 프로듀서와 소비자가있는 코드를 만들려고했습니다. 나는 생산자와 소비자를 위해 다중 스레드를 만들고 동기화를 위해 세마포어를 사용했다. 이 코드는 단일 제작자와 소비자가 잘 작동했습니다.멀티 프로듀서 - 소비자 실행의 효율성

제가 직면 한 문제는 프로그램 실행 시간이 지나면 consumer1과 producer1 만 프로세스에 참여한다는 것입니다. 나는 다른 생산자와 소비자에게 무슨 일이 일어 났는지 이해할 수 없다.

멀티 프로듀서 - 소비자 문제를 효율적으로 만드는 방법을 알고 싶습니다. 모든 생산자와 소비자가 각각 생산하고 소비 할 동등한 기회를 얻는다는 의미에서 효율적입니까? C++ 코드는 (그것은 C를 많이 포함) :

#include <iostream> 
#include <pthread.h> 
#include <semaphore.h> 
#include <unistd.h> 
#include <queue> 
using namespace std; 
sem_t empty; 
sem_t full; 
int cnt = 0; 
pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER; 
queue<int> q; 
void *producer(void *a) 
{ 
    int *num = (int *)a; 
    while(1) { 
     sem_wait(&empty); 
     pthread_mutex_lock(&mutex); 
     cnt = cnt+1; 
     q.push(cnt); 
     cout<<cnt<<" item produced by producer "<<(*num+1)<<endl; 
     pthread_mutex_unlock(&mutex); 
     sem_post(&full); 
     sleep(1); 
    } 
} 
void *consumer(void *a) 
{ 
    int *num = (int *)a; 
    while(1) { 
     sem_wait(&full); 
     pthread_mutex_lock(&mutex); 
     cout<<q.front()<<" item consumed by consumer "<<(*num+1)<<endl; 
     q.pop(); 
     pthread_mutex_unlock(&mutex); 
     sem_post(&empty); 
     sleep(1); 
    } 
} 
int main() 
{ 
    pthread_t p[5]; 
    pthread_t c[5]; 
    sem_init(&empty,0,5); 
    sem_init(&full,0,0); 
    int i; 
    for(i = 0; i < 5; i++) { 
     pthread_create(&p[i],NULL,producer,(void *)(&i)); 
    } 
    for(i = 0; i < 5; i++) { 
     pthread_create(&c[i],NULL,consumer,(void *)(&i)); 
    } 
    for(i = 0; i < 5; i++) { 
     pthread_join(p[i],NULL); 
     pthread_join(c[i],NULL); 
    } 
} 

업데이트 코드 :

#include <iostream> 
#include <pthread.h> 
#include <semaphore.h> 
#include <unistd.h> 
#include <queue> 
#include <map> 
using namespace std; 
sem_t empty; 
sem_t full; 
int cnt = 0; 
pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER; 
map<pthread_t,int> mc,mp; 
queue<int> q; 
void *producer(void *a) 
{ 
    while(1) { 
     sem_wait(&empty); 
     pthread_mutex_lock(&mutex); 
     cnt = cnt+1; 
     q.push(cnt); 
     cout<<cnt<<" item produced by producer "<<mp[pthread_self()]<<endl; 
     pthread_mutex_unlock(&mutex); 
     sem_post(&full); 
     sleep(1); 
    } 
} 
void *consumer(void *a) 
{ 
    while(1) { 
     sem_wait(&full); 
     pthread_mutex_lock(&mutex); 
     cout<<q.front()<<" item consumed by consumer "<<mc[pthread_self()]<<endl; 
     q.pop(); 
     pthread_mutex_unlock(&mutex); 
     sem_post(&empty); 
     sleep(1); 
    } 
} 
int main() 
{ 
    pthread_t p[5]; 
    pthread_t c[5]; 
    sem_init(&empty,0,5); 
    sem_init(&full,0,0); 
    int i; 
    pthread_mutex_lock(&mutex); 
    for(i = 0; i < 5; i++) { 
     pthread_create(&p[i],NULL,producer,NULL); 
     pthread_create(&c[i],NULL,consumer,NULL); 
     mc[c[i]] = i+1; 
     mp[p[i]] = i+1; 
    } 
    pthread_mutex_unlock(&mutex); 
    for(i = 0; i < 5; i++) { 
     pthread_join(p[i],NULL); 
     pthread_join(c[i],NULL); 
    } 
} 
+0

pthread_create에 전달할 인수를 살펴보고 스레드 함수에서 해당 정보를 사용하는 방법을 살펴보십시오. 경쟁 조건이 있는지 고려하십시오. 'i'를 전달할 때와 'i'를 전달할 때의 영향을 고려하십시오. –

+0

표준'std :: thread' 대신 OS 특정'pthread' 사용을 정당화하는 이유는 무엇입니까? – Christophe

+0

@Christophe : C++로 쓰레드를 배웠던 적이 없기 때문에 C 버전을 사용했습니다. –

답변

2

짧은 대답

스레드 사실 평등 한 기회와 함께 실행하지만, 단지 그들이 않습니다 그 식별자가 아닌 식별자를 출력하십시오.

자세한 설명

당신은 스레드 번호에 각 스레드에 대한 포인터 num을 유지한다. 값 자체가 아닌 저장된 값에 대한 포인터입니다. 그래서 모든 스레드는 동일한 카운터를 가리키며 자신의 식별자를 찾으려고합니다.

*num에 액세스 할 때마다 스레드를 시작했을 때 i의 값이 아닌 현재 값에 액세스 할 수 있습니다.

main()의 모든 루프에서 i 변수를 다시 사용합니다. 따라서 마지막 루프는 i0으로 다시 설정하고 첫 번째 스레드가 결합 될 때까지 기다립니다. 그러나 이러한 모든 스레드는 영원히 반복되므로 루프는이 초기 0 값을 초과 할 기회를 거의 얻지 못합니다. 그래서 모든 스레드는 숫자 *num+1이 현재 1이라고 생각합니다.

메모에 지적 된 사람이 경쟁 조건을 만드는 방식에 유의하십시오. 모든 소비자 및 제작자 스레드는 포인터를 참조 해제하여 뮤텍스 보호 영역에서 동일한 변수에 액세스합니다. 괜찮아. 그러나 변수를 읽는 동안 주 스레드는 여전히 모든 잠금 외부에서 공유 변수를 변경할 수 있습니다. 이것은 확실히 인종의 위험입니다. 각 스레드 ID입니다 자체의 변경되지 않은 복사본을 가질 수 있도록

해결하는 것은

std::thread, 당신은 walue에 의해 i을 전달할 수 있도록한다.

pthreads를 사용하면 값에 대한 포인터를 전달해야합니다. 불행히도, 여러분이 쓰레드의 시작 부분에있는 값의 로컬 사본을 사용하더라도 여전히 경쟁 조건에 처해있을 것입니다.

어떤 스레드가 실제로 작업 중인지 관찰하는 빠른 해결 방법은 pthread_self() (결과를 here 참조)의 결과도 출력하는 것입니다. 또는 배열을 int 배열에 저장하고 각 스레드에 해당 배열의 고유 요소에 대한 주소를 전달하십시오.

+0

thread_join이 초기 값을 어떻게 바꿀까요? 나는 그것을 얻지 못 하느냐? –

+0

@ShivamMitra thread_join은 아무 것도 변경하지 않지만'for (i = 0;')로 시작하는 루프에서 처리합니다. 불행히도 모든 num 포인터 ar이 가리키는 것과 동일합니다. – Christophe

+0

제안 사항 –

관련 문제