2016-08-31 3 views
2


나는 C++ 11에서 동시성을 배우기 위해 많은 실수를 저지르고있다. 나는 이것을 물어봐야 만한다.

다음은이 하나이다. 하나의 큐와 세 개의 쓰레드 중 하나는 정수를 큐에 넣고, 다른 두 개는 그에 상응하여 s1, s2를 대기열에있는 숫자의 총 합계를 얻을 수 있도록 대기열을 팝핑합니다. 간단하게하기 위해 1에서 10까지 숫자를 대기열에 넣었습니다.

가끔은 작동하지만 때로는 무한 루프가있는 것처럼 보입니다. 이유는 무엇입니까? 함수 P1 라인에서

C++ 11 condtional variable

#include <queue> 
#include <memory> 
#include <mutex> 
#include <thread> 
#include <iostream> 
#include <condition_variable> 
#include <string> 

class threadsafe_queue { 
private: 
    mutable std::mutex mut; 
    std::queue<int> data_queue; 
    std::condition_variable data_cond; 
    std::string log; //just to see what is going on behind 
    bool done; 

public: 
    threadsafe_queue(){ 
     log = "initializing queue\n"; 
     done = false; 
    } 
    threadsafe_queue(threadsafe_queue const& other) { 
     std::lock_guard<std::mutex> lk(other.mut); 
     data_queue = other.data_queue; 
    } 
    void set_done(bool const s) { 
     std::lock_guard<std::mutex> lk(mut); 
     done = s; 
    } 
    bool get_done() { 
     std::lock_guard<std::mutex> lk(mut); 
     return done; 
    } 
    void push(int new_value) { 
     std::lock_guard<std::mutex> lk(mut); 
     log += "+pushing " + std::to_string(new_value) + "\n"; 
     data_queue.push(new_value); 
     data_cond.notify_one(); 
    } 
    void wait_and_pop(int& value) { 
     std::unique_lock<std::mutex> lk(mut); 
     data_cond.wait(lk, [this]{return !data_queue.empty();}); 
     value = data_queue.front(); 
     log += "-poping " + std::to_string(value) + "\n"; 
     data_queue.pop(); 
    } 
    std::shared_ptr<int> wait_and_pop() { 
     std::unique_lock<std::mutex> lk(mut); 
     data_cond.wait(lk, [this]{return !data_queue.empty();}); 
     std::shared_ptr<int> res(std::make_shared<int>(data_queue.front())); 
     log += "- popping " + std::to_string(*res) + "\n"; 
     data_queue.pop(); 
     return res; 
    } 
    bool try_pop(int& value) { 
     std::lock_guard<std::mutex> lk(mut); 
     if (data_queue.empty()) { 
      log += "tried to pop but it was empty\n"; 
      return false; 
     } 
     value = data_queue.front(); 
     log += "-popping " + std::to_string(value) + "\n"; 
     data_queue.pop(); 
     return true; 
    } 
    std::shared_ptr<int> try_pop() { 
     std::lock_guard<std::mutex> lk(mut); 
     if (data_queue.empty()) { 
      log += "tried to pop but it was empty\n"; 
      return std::shared_ptr<int>(); 
     } 
     std::shared_ptr<int> res(std::make_shared<int>(data_queue.front())); 
     log += "-popping " + std::to_string(*res) + "\n"; 
     data_queue.pop(); 
     return res; 
    } 
    bool empty() const { 
     std::lock_guard<std::mutex> lk(mut); 
     //log += "checking the queue if it is empty\n"; 
     return data_queue.empty(); 
    } 

    std::string get_log() { 
     return log; 
    } 

}; 

threadsafe_queue tq; 
int s1, s2; 

void prepare() { 
    for (int i = 1; i <= 10; i++) 
     tq.push(i); 
    tq.set_done(true); 
} 

void p1() { 
    while (true) { 
     int data; 
     tq.wait_and_pop(data); 
     s1 += data; 
     if (tq.get_done() && tq.empty()) break; 
    } 
} 

void p2() { 
    while (true) { 
     int data; 
     tq.wait_and_pop(data); 
     s2 += data; 
     if (tq.get_done() && tq.empty()) break; 
    } 
} 

int main(int argc, char *argv[]) { 
    std::thread pp(prepare); 
    std::thread worker(p1); 
    std::thread worker2(p2); 
    pp.join(); 
    worker.join(); 
    worker2.join(); 

    std::cout << tq.get_log() << std::endl; 
    std::cout << s1 << " " << s2 << std::endl; 
    return 0; 
} 
+0

'done'을 설정할 때'data_cond'를 통보하고'data_cond.wait' 조건에서 그것을 확인해야 할 것입니다. – dewaffled

+0

'push()'가 대기 상태에서'wait_and_pop()'을 비우기 위해'notify_one'을 호출하기 위해 뮤텍스를 지나갈 수 없으므로'wait()'이 호출되기 전에'wait_and_pop() 뮤텍스 잠금을 잡기 전에 기다려야한다고 생각합니다. –

+0

@ KenY-N, 네, 그게 제가 의심했던 것입니다. 하지만 push()가 어떤 것보다 먼저 작동해야하는지 어떻게 알 수 있습니까? – Nano

답변

1

찾는 5

(tq.get_done() & & tq.empty()) 어기면;

대기열이 비 었는지 확인했습니다. 그렇지 않았습니다. 이제 반복하고 입력하십시오

tq.wait_and_pop (데이터); 당신이

data_cond.wait 찾을 수 있습니다

(LK를, [이] {반환 data_queue.empty를();!}); 본질적

동안 (data_queue.empty()) { 대기 (LK)이다

; }

'!'가 없습니다.

이제 스레드가 대기하고 비어 있지 않은 대기열을 기다립니다. 생산자 ID가 대기열을 채우므로 완료되지 않습니다. 스레드는 절대로 결합하지 않습니다.

해결 방법에는 여러 가지가 있습니다. 나 혼자만 찾을 수있을거야.

+0

아 그래, 그건 사실이야. 'p2'가'p1'의'tq.empty()'와 다음'tq.wait_and_pop (data); 사이의 큐를 비울 경우 또 다른 중단이 있습니다. –

+0

멋지다! 고맙습니다. – Nano

+0

@Nano : 내 대답이 내 질문에 만족하면 내 대답 옆에 녹색 작은 진드기를 추가하는 것이 좋습니다. 타이 – PanicSheep