2009-09-09 4 views
2

여러 제작자, 단일 소비자 모델 (여러 스레드가 단일 파일 작성자 스레드에 메시지 보내기)가있는 응용 프로그램을 작성하고 있습니다.여러 제작자/단일 소비자 모델에서 최적의 휴면 시간

각 제작자 스레드에는 쓰기 작업을 수행하는 대기열과 소비자가 읽을 수있는 대기열이 있습니다. 소비자 스레드의 모든 루프는 각 제작자를 반복하고 해당 제작자의 뮤텍스를 잠그고 큐를 스왑하고 잠금을 해제하고 제작자가 더 이상 사용하지 않는 큐에서 씁니다.

소비자 스레드의 루프에서 모든 생산자 스레드를 처리 한 후 지정된 시간 동안 대기합니다. 내가 즉각적으로 알아 차린 사실 중 하나는 제작자가 대기열에 무언가를 쓰고 리턴하는 속도가 (1x 생산자 스레드에서 2로 이동할 때) 5 배로 급격히 증가했다는 것입니다. 더 많은 스레드가 추가되면이 평균 시간은 하단 아웃 - 10 명의 프로듀서 대 15 명의 프로듀서와의 시간 차이는 크지 않습니다. 이는 더 많은 프로듀서가 처리 할 때 프로듀서 스레드의 뮤텍스에 대한 경합이 적기 때문일 수 있습니다.

불행히도, < 5 개의 생산자를 갖는 것은 응용 프로그램에 대한 상당히 일반적인 시나리오이며, 수면 시간을 최적화하여 생산자가 얼마나 많은지에 관계없이 합리적인 성능을 얻고 싶습니다. 나는 수면 시간을 늘림으로써 낮은 생산자 수에 대해서는 더 나은 성능을 얻을 수 있지만 많은 생산자 수에 대해서는 더 나쁜 성능을 얻을 수 있음을 발견했습니다.

다른 사람이이 문제가 발생 했습니까? 그렇다면 해결책은 무엇입니까? 스레드 수와 함께 절전 시간을 늘리려고했지만 다소 특정 시스템의 예심과 오류가있는 것처럼 보입니다.

답변

2

생산자 수를 기준으로 수면 시간을 선택하거나 수유 시간을 일부 다이나믹 방식에 따라 조정할 수도 있습니다. 소비자가 깨어나고 일을하지 않으면 수면 시간을 두 배로하십시오. 그렇지 않으면 절반으로 줄입니다. 그러나 수면 시간을 최소 및 최대로 제한하십시오.

어느 것이 든 더 근본적인 문제를 종이로 작성하고 있습니다. 수면과 폴링은 올바르게 진행되기 쉽고 때로는 유일한 접근법이지만 가능한 많은 단점이 있으며 "올바른"방식은 아닙니다.

생산자가 항목을 대기열에 추가 할 때마다 증가하고 소비자가 대기열의 항목을 처리 할 때 감소하는 세마포어를 추가하여 올바른 방향으로 향할 수 있습니다. 소비자는 처리 할 품목이있을 때만 깨어나 즉시 처리 할 것입니다.

대기열 폴링은 여전히 ​​문제가 될 수 있습니다. 항목이있는 대기열을 참조하는 새 대기열을 추가 할 수 있습니다.그러나 오히려 생산자 당 대기열보다는 소비자가 처리하는 단일 대기열이없는 이유에 대한 질문이 제기됩니다. 그 밖의 모든 것이 평등 한 것은 최선의 접근 방식처럼 들립니다. 대신 수면의

+0

1 : 근본적인 문제는 먼저 –

+0

내가 하나의 큐가 내 주요 우선 순위는 큐에 생산 기록을 가지고와 (코드입니다 가능한 한 빨리 완료 할 수있는 것을했다하지 않은 이유에 대해 이야기한다 로깅 라이브러리). 어쩌면 내가 이것을 overteninking하고 하나의 대기열 * 같은 * 빠른 것입니다 -하지만 이상적으로 로거 어디서나 몇 40-50 스레드에서 지원할 것입니다. 많은 큐의 경우 하나의 큐 잠금을 위해 경합하는 40-50 개의 스레드 대신 동일한 리소스에 대해 2 개의 스레드가 경쟁합니다. 세마포어에 대한 아이디어가 마음에 들지만, 아마 그 일에 대해 "올바른"방식이라고 생각합니다. –

0

프로그래밍에 사용하는 언어로 차단 대기열 구현을 찾으십시오. 어떤 수의 생산자와 한 명의 소비자에 대해서도 대기열은 하나만 있으면 충분합니다.

+0

내 주요 목표는 메시지를 보낼 프로듀서 스레드 소요되는 시간을 최소화하는 것입니다 같은 단일 큐에 고정하기 위해 모든 생산을 강제하는 것을 피하기하고 싶습니다. 단일 차단 대기열로 이동하면이 동작이 강제 적용됩니다. –

0

내게는 실수로 다른 사람이 잠들거나 실제 작업을하는 소비자 스레드를 사용하여 버퍼링을 도입 한 것처럼 들립니다. (버퍼 역할을하는 큐) 아마도 생산자 측에서 간단한 버퍼링을하면 경쟁이 줄어들 것입니다.

당신의 시스템은 생산자와 소비자 사이의 lock-contention에 매우 민감한 것 같지만 그런 간단한 스왑 작업이 실행 통계에 나타나기에 충분한 CPU 시간을 차지하는 이유에 대해 당황 스럽습니다.

일부 코드를 표시 할 수 있습니까?

편집 : 할 일이 없을 때에도 잠금 및 대기열을 바꿀 수 있습니까?

+0

흠, 나는 당신이 무엇을 의미하는지 완전히 모르겠습니다. 생산자의 뮤텍스를 유지해야하는 시간을 최소화하기 위해 대기열이 소비자에 의해 스왑된다는 의미에서 의도적 인 버퍼링이 있습니다. 이 정말 및 붙여 넣기 코드를 복사하지 않도록 관련 업무이지만, 스왑 작업이 당신이 기대 정확히 같은 두 개의 포인터 스왑 - 온도 = queue2, queue2 = queue1을, queue1을 = 온도를. 멋진 것은 없으며 뮤텍스는 해제되기 전에 해당 작업에 대해서만 개최됩니다. –

+0

예, 문제가 잠금 경합 인 경우 잠금을 획득하려고 시도하는 횟수를 줄이려면 버퍼링 (제작자에게 현지)이 필요합니다. –

+0

도 @George 필립스의 대답을 고려해야합니다. 어쩌면 당신의 계획을 바꿀 필요가있을 것입니다. –

1

, 나는 조건에 당신의 소비자 블록은 생산자에 의해 신호하는 것이 좋습니다 것입니다. posix 호환 시스템에서는 pthread_cond로 작동하도록 만들 수 있습니다. pthread_cond_t의 배열을 각 제작자마다 하나씩 만든 다음 둘 사이에 공유되는 추가 글꼴을 만듭니다. 생산자는 먼저 개별 조건 변수와 공유 변수를 전달합니다. 소비자는 공유 조건을 기다린 다음 배열 요소를 반복하여 배열의 각 요소에 pthread_cond_timed_wait()을 수행합니다 (pthread_get_expiration_np()을 사용하여 "지금"의 절대 시간을 가져옵니다). wait가 0을 반환하면 해당 제작자는 데이터를 기록합니다. 소비자는 다시 기다리기 전에 조건 변수를 다시 초기화해야합니다. 차단 대기를 사용하여

, 당신은 소비자가 불필요하게 잠금 아웃 된 생산자 양의 시간을 최소화 할 수 있습니다. 이전 답변에서 언급 한 것처럼 세마포어로이 작업을 수행 할 수도 있습니다. 세마포어는 조건에 비해 시맨틱을 단순화 시켰지만 제 생각에는 공유 루프를 통과 할 때마다 처리 된 각 제작자에 대해 공유 세마포어를 한 번 줄이십시오. 조건 변수는 신호를받은 후에 다시 초기화하면 부울 세마포어처럼 기본적으로 사용할 수 있다는 이점이 있습니다.