2017-11-17 14 views
4

블로킹 큐가 있습니다. (실제로 구현을 변경하기가 어려울 수 있습니다.) 실제로 블럭을 테스트하려고합니다. 특히 pop 메서드는 대기열이 비어 있고 push이 수행되는 즉시 차단을 해제해야하는 경우 차단해야합니다. 테스트를 위해 다음과 같은 의사 C++ (11) 코드를 참조하십시오 : 분명히블로킹 큐의 실제 블럭을 테스트하는 방법

BlockingQueue queue; // empty queue 

thread pushThread([] 
{ 
    sleep(large_delay); 
    queue.push(); 
}); 

queue.pop(); 

그것을 전체 스레드 pushThread가 호출되어 실행되고 pop 전에 종료되는 일이 발생할 수 있기 때문에, 완벽하지, 지연이 큰 경우에도 , 지연이 클수록 테스트가 끝날 때까지 기다려야합니다.

push이 호출되기 전에 pop이 실행되고 그 결과가 push이 반환 될 때까지 차단되는지 어떻게 적절하게 확인할 수 있습니까?

+0

일반적으로 다른 스레드의 경우 차단 스레드는 스케줄러에서 절전 모드로 전환 한 스레드와 동일하게 보이므로 표준 C++에서는 사용할 수 없습니다. 실질적으로 말해서, 50ms 정도 기다리는 것만으로는 충분하지 않습니다. – MikeMB

답변

0

이 작업을 수행하려면 std::condition_variable을 사용할 수 있습니다. cppreference.com의 도움말 페이지는 실제로 당신이 찾고있는 것이어야하는 매우 훌륭한 cosumer-producer 예제를 보여줍니다. http://en.cppreference.com/w/cpp/thread/condition_variable

EDIT : 실제로 cppreference.com의 독일어 버전은 더 좋은 예입니다 .-) http://de.cppreference.com/w/cpp/thread/condition_variable

+0

어디에서 기다리시겠습니까? 어디에서 신호를 보내겠습니까? – Nick

+0

두 개의 스레드를 시작하십시오. 하나는'pop()'을위한 것이고 다른 하나는'push'를위한 것입니다. 'pop()'을 기다리고'push()'다음에 신호를 보낸다. 'push()'쓰레드 다음에'push()'쓰레드를 시작하도록하라. –

+1

@SamerTufail 문제를 해결하지 않고 복잡성을 추가 한 것 같습니다. 솔루션에서 pop이 호출 된 후 push가 호출되도록하려면 어떻게합니까? – Nick

1

BlockingQueue에 추가 상태 및 인터페이스를 추가하지 않으면 이것이 가능하지 않다고 생각합니다.

증명은 이렇게됩니다. pop에서 읽기 스레드가 차단 될 때까지 기다리고 싶습니다. 그러나 스레드와 스레드를 구분할 방법이 없습니다. pop. pop 호출 전이나 후에 입력 한 내용이 그대로 유지됩니다.

이 문제를 100 %의 신뢰성으로 해결하려면 대기열의 뮤텍스를 지키는 대기열에 상태를 추가해야합니다. 즉, "다른 사람이 대기 중임"을 의미합니다. pop 호출은 mutex를 원자 적으로 해제하고 내부 조건 변수에서 절전 모드로 전환하기 직전에 해당 상태를 업데이트해야합니다. push 스레드는 뮤텍스를 가져 와서 "누군가 기다리고 있습니다"때까지 기다릴 수 있습니다. 여기에서 통화 중 루프를 피하려면 조건 변수를 다시 사용하는 것이 좋습니다.

이 기계류는 모두 큐 자체만큼 복잡하기 때문에 테스트 할 수도 있습니다. 이런 종류의 멀티 스레드 코드는 "코드 적용 범위"와 같은 개념입니다. 단위 테스트 자체 - 조금 분해하십시오. 가능한 한 많은 수의 인터리브가 있습니다.

실제로, 나는 원래 자고있는 방식으로 갈 것입니다.

1
template<class T> 
struct async_queue { 
    T pop() { 
    auto l = lock(); 
    ++wait_count; 
    cv.wait(l, [&]{ return !data.empty(); }); 
    --wait_count; 
    auto r = std::move(data.front()); 
    data.pop_front(); 
    return r; 
    } 
    void push(T in) { 
    { 
     auto l = lock(); 
     data.push_back(std::move(in)); 
    } 
    cv.notify_one(); 
    } 
    void push_many(std::initializer_list<T> in) { 
    { 
     auto l = lock(); 
     for (auto&& x: in) 
     data.push_back(x); 
    } 
    cv.notify_all(); 
    } 
    std::size_t readers_waiting() { 
    return wait_count; 
    } 
    std::size_t data_waiting() const { 
    auto l = lock(); 
    return data.size(); 
    } 
private: 
    std::queue<T> data; 
    std::condition_variable cv; 
    mutable std::mutex m; 
    std::atomic<std::size_t> wait_count{0}; 
    auto lock() const { return std::unique_lock<std::mutex>(m); } 

}; 

또는 somesuch. 푸시 스레드에서

readers_waiting에 바쁜 대기는 어떤에서 당신이 자물쇠를 잠금이 해제되기 전에 cv.wait 내에있는 포인트 1.

을 통과 할 때까지. push하십시오.

이론상 무한히 느린 독자 스레드는 cv.wait에 들어갔을 수 있으며 push이라고 부르는 시간까지 첫 번째 람다를 평가할 수 있지만 매우 느린 판독기 스레드는 차단 된 것보다 다르지 않습니다 ...

그러나 느린 스레드 시작 등을 처리합니다.

디버깅 이외의 다른 용도로는 readers_waitingdata_waiting을 사용하는 것이 일반적으로 코드 냄새입니다.

+0

OT :'unique_lock '또는'lock_guard'를 "가장 짜증나는 구문 분석"과 관련하여 개인적 (나쁜) 경험으로부터 추출한 것입니다. –