2017-03-15 5 views
2

멀티 스레딩에 대해 배우고 있으며 제작자 - 소비자 문제 (이를 호출 할 수있는 경우 세마포어 사용)를 시뮬레이트하고 싶습니다.Segfault가 대기열을 동기화하려고 시도 중임

대기열을 보유하는 클래스가 있고, 생산자가 대기열에 int를 푸시하고 소비자가 가져 와서 인쇄합니다. I 시뮬레이션

class TestClass{ 
public: 
    void producer(int i){ 
     unique_lock<mutex> l(m); 
     q.push(i); 
     if(q.size()) 
      cnd.notify_all(); 
    } 

    void consumer(){ 
     unique_lock<mutex> l(m); 
     while(q.empty()){ 
      cnd.wait(l); 
     } 
     int tmp = q.front(); 
     q.pop(); 
     cout << "Producer got " << tmp << endl; 
    } 
    void ConsumerInit(int threads){ 
     for(int i = 0; i < threads; i++){ 
      thrs[i] = thread(&TestClass::consumer, this); 
     } 
     for(auto &a : thrs) 
      a.join(); 
    } 


private: 
    queue<int> q; 
    vector<thread> thrs; 
    mutex m; 
    condition_variable cnd; 
}; 

다음으로 내가 호출 작은 콘솔 애플리케이션을 사용하는 데이터 :

int main(){ 
    int x; 
    TestClass t; 
    int counter = 0; 
    while(cin >> x){ 
     if(x == 0) 
      break; 
     if(x == 1) 
      t.producer(counter++); 
     if(x == 2) 
      t.ConsumerInit(5); 
    } 
} 

그래서 사용자 프레스 2 개 스레드가있는 경우, 사용자 입력 (1), 데이터는 대기열에 가압 될 때 산란했다.

예를 들어, 1을 누른 다음 2를 누르거나 2 1 1 을 누르는 등의 순서로 호출하면 segfault가 throw됩니다. 나는 왜 내 코드에 대한 나의 이해가 다음과 같은지 잘 모르겠다. 2 1 1

5 개의 스레드를 초기화하면 대기열이 비어있어 잠자기 상태가된다. 숫자를 대기열로 보내면 모든 스레드가 대기 중임을 알립니다. 잠긴 뮤텍스를 다시 깨우고 대기열에서 번호를 검색 한 후 뮤텍스를 릴리스하면 뮤텍스가 릴리스 될 때 다른 스레드가 동일한 작업을 수행하고 뮤텍스를 잠금 해제합니다. 뮤텍스가 풀린 후 세 번째 스레드는 계속 루프 상태이며 보입니다. 그 큐는 아직 다시 비어 있고, 나머지 모든 쓰레드와 마찬가지로 다시 잠든다.

이 논리가 맞습니까? 그렇다면 왜 이것이 segfault를 던지는지, 그렇지 않다면 나는 모든 설명에 감사 드린다.

도움 주셔서 감사합니다.

// 편집 대답 suggets에 의해 []를 vector.push_back으로 바꿨지 만 소비자는 데이터를 사용하지 않고 가져 가거나 인쇄하지 않습니다. 당신이

thrs[i] = thread(&CTest::consumer, this); 

을 수행 할 때

+0

오타, 감사합니다! 업데이트 됨 – Darlyn

+0

오, 고마워요. 다시 말하면 – Darlyn

답변

1

당신은 당신은 충돌이 될 것이다 어디 그

thrs.emplace_back(&CTest::consumer, this); 

을해야 THRS 벡터 확장되지 않습니다.

+0

은 그것을 알아 차리고 push_back을 추가했으나 소비자는 지금 아무런 데이터도 가지고 있지 않습니다. – Darlyn

+0

대기중인 입력이 5 개 (따라서 프로그램이 더 이상 입력을 할 수 없음)가 아니면 ConsumerInit이 무기한 차단됩니다. "2 1 1"을 (를) 입력 하시겠습니까? –

+0

사용자 inpu는 2 1 1이므로 스레드를 생성 한 다음 대기열에서 두 번 밀어 넣으십시오. – Darlyn

1

문제점은 멀티 스레딩과 관련이 없습니다.

for (int i = 0; i < threads; i++) { 
     thrs[i] = thread(&CTest::consumer, this); 

    //... 
    vector<thread> thrs; 

thrs 벡터 빈이며,이 항목을 가지고있는 것처럼 당신이 액세스하려는 : 당신은 std::vector 범위를 벗어날 액세스하는.

오류, 사용 표시하려면

 thrs.at(i) = thread(&CTest::consumer, this); 

을 당신은 std::out_of_range 예외 대신 세그멘테이션 결함으로 맞이합니다.

+0

은 그것을 알아 차리고 push_back을 추가했지만 소비자는 이제 데이터로 아무 것도하지 않으며 그것을 가져 와서 인쇄하지 않습니다. – Darlyn

+0

그런데 또 다른 문제입니다. – PaulMcKenzie

1

입력 시퀀스가 ​​1 1 1 1 1 ... 2의 형식이 아닌 경우 프로그램이 교착 상태가됩니다. 즉, 앞에 1s이 올 경우 숫자가 5보다 작 으면됩니다.큐 크기의 총 요소가 5 미만이며, 메인 스레드가 작성한 5 개 소비자 스레드의 일부 요소를받을 수있는 큐를 기다리고 차단, consumerInit를 호출하면

: 여기

는 이유입니다. 한편, 주 스레드는 join 작업을 차단합니다. 메인 쓰레드는 소비자 쓰레드가 데이터를 기다리는 쓰레드를 기다리는 동안 기다릴 것이기 때문에 아무런 진전이 없을 것이다. 따라서 교착 상태.

1

문제는 여기에 있습니다 : 당신은 소비자가 끝날 때까지 기다리고 2를 입력 한 후

for(auto &a : thrs) 
     a.join(); 

메인 스레드가 여기 차단됩니다. 따라서이 시점 이후에는 입력 내용을 입력하는 것으로 생각되지만 cin은 발생하지 않습니다.

이 두 줄을 제거한 다음 1을 입력하면 producer/consumer가 작업을 수행합니다.