스레드 동기화 문제 중에, 생산자 소비자 문제가 있습니다. 문제는 매우 간단합니다. 생산자는 물건을 생산합니다. 소비자는 원하는 상품이 있으면 가져갑니다. 그렇지 않으면 기다립니다. productor는, 생산된 물품이 없으면 만듭니다. 이런 문제를 어떻게 해결하면 좋을까요?

 

  일단, 물품이 있는지 없는지 관리하는 변수는 state입니다. 이 값이 1이면 있다는 것이고, 0이면 없다는 것입니다. 이것은 두 쓰레드가 동시에 접근해서 read, write를 하면 안 되는 변수라고 할 수 있어요. 즉, 물건이 있을 때, 꺼내 가려고 할 때, lock을 걸어야 하고, buffer에 집어넣으려고 할 때에도 lock을 걸어야 합니다.

 

 


 그러면, 소비자 Thread는 다음과 같이 설계할 수 있습니다.

 

 생산자 Thread는 어떻게 하면 좋을까요? 당연하게도 state가 0이면 물건을 생산해야 하니 state가 1이 되게 해야 할 거에요.

 

 

 문제는, 이 buffer 내에 있는, state는 생산자와 소비자, 이 두 Thread가 공통으로 접근하는 공유 변수입니다. 그러면 물건을 넣는 put과 소비자가 물품을 꺼내는 get 메서드가 호출이 될 때, lock을 걸어야 할 거에요. 그런데, 이게 끝이 아닙니다. 생산자가 n개를 생산하면 소비자 또한 n개를 가지고 가야 하는데, 이대로 해 버리면, 문제의 의도대로 동작하지 않거든요.

 

 

 그러면 어떻게 하면 될까요? 생각보다 간단합니다. get 메서드의 경우, buffer의 state가 1이 아니라면, 1이 될 때 까지 기다리면 됩니다. 그러면 put은 어떨까요? state가 0이 될 때 까지 기다리면 됩니다.

 

 

  그런데, 문제점이 하나 있습니다. put의 경우에는 state가 0이 될 때 까지 기다린다고 했는데, 어떻게 wait를 해야 할까요? 보시면, 일단 wait until ~ 로 미루어 보아서는 wait 함수를 써야 할 듯 싶습니다. 스레드 편에서 wait하고 sleep의 차이를 설명해 드린 적이 있었습니다.

 

 

[관련글]

sleep vs wait 이 둘의 차이는 무엇일까요?

 

 

 기억이 나실지는 모르겠지만요. sleep은 lock을 물지만, wait는 lock을 물지 않습니다. 다른 Thread가 lock을 획득할 수 있게 합니다. 그러면, put 메서드에서, state가 0일 때, Consumer가 lock을 release 하고, Producer가 lock을 획득해서, 물건을 넣어야 할 겁니다.

 

 마찬가지로 state가 1인 경우, Producer가 lock을 획득했다면, release하고, Consumer가 가져가게 하면 좋겠네요.

 

 

 그러면 이를 고려했을 때, put은 이렇게 작성할 수 있을 거에요. state가 1인 경우에, 소비자가 물건을 가져가게 하기 위해서, wait 함수를 호출합니다.

 

 

 마찬가지로, get의 경우, 물건이 없을 때, 생산자가 buffer에 물건을 넣게 하기 위해서, lock을 release 합니다. 그러면 생산자가 버퍼에 잘 넣어줄 겁니다. 문제는 이 wait의 경우, Interrupt가 되거나, 깨어나라는 메세지가 오지 않는 한 block이 걸려버립니다. 즉, 이렇게만 코딩을 하면 block이 되어 버립니다.

 

 

 put을 했는데, 지금 get을 하지 못하고 있어요. 이는, wait를 호출하고 나서, 계속 block이 되고 있기 때문이에요. block. 뭔가 생소한 것이 나왔는데요. 의외로 많이 써보셨을 거에요. 대표적으로 scanf라던지 Java의 nextInt와 같은 것들이 그 예입니다. 이것들은 입력을 받을 때 까지, 진행이 되지 않아요. 하튼, 그러면 어떻게 해야 할까요? 생산자가 생산을 완료했다면,소비자에게 물건이 생산되었다고 해야 합니다. 

 

 

 역으로 소비자가 물건을 가져갔다면 어떻게 해야 하나요? 만약에 productor 스레드가 wait 상태라면 실행 대기 큐에 넣어야 할 겁니다. 그러한 일을 하는 함수는 notify입니다.

 

 


 제가 설명한 것을 토대로, 코드를 작성해 봅시다.

 

 

 먼저, get이던 put이던, 함수가 끝나기 직전에 notify를 호출합니다. 이는, 어떠한 경우라도, 끝나기 직전에는 get의 경우에는 소비자가 물건을 가져간 경우이므로, consumer가 물품을 가져갔다고 알려줍니다. 문제에서 나오는 것은, main Thread와, C, P인데요. C가 해야 할 일을 마쳤으므로, P가 wait 상태라면 실행 대기 큐에 넣습니다.

 

 

 그러면 생산자가 호출하는 put 메서드도 마찬가지로 작성하면 될 거에요. 이를 그대로 코드로 구현해 봅시다.

 

 

 제가 설명한 부분이 그대로 다 나타나 있어요.

 

 

 순서 또한 vaild 하게 지켜졌다는 것을 알 수 있어요.