Asynchronous Agents Library로 Dining Philosophers 문제 해결하기 - 마지막회
VC++ 10 Concurrency Runtime 2009. 10. 24. 01:30Asynchronous Agents Library로 Dining Philosophers 문제 해결하기 - 1
Asynchronous Agents Library로 Dining Philosophers 문제 해결하기 - 2
오래 기다리셨습니다; 그간 일이 바빠서;; 어쨌든 지난번에 Concurrecy::agent 에서 상속받은 Philosopher 클래스를 살펴봤었죠. 아래 두 함수만 제외하고 말입니다.
자 먼저 젓가락을 집는 함수입니다. 젓가락 한쌍을 동시에 집어야지 하나만이라도 먼저 집으려고 하다간 서로 젓가락 하나씩 잡고 기다리는 데드락 상황이 발생할 수 있습니다. 이를 위해 쓰이는 것이 지난 회에 잠깐 언급했든 join 메시지 블록입니다. 그 중에서도 non_greedy 버전을 사용해야 합니다. non_greedy 버전은 명시된 타겟을 모두 얻을 수 있을 때에만 실제 획득을 시도합니다. gready 버전을 사용하면 전술한 것처럼 데드락이 발생할 수 있습니다.
73 std::vector<Chopstick*> PickupChopsticks()
74 {
75 //join 생성
76 Concurrency::join<Chopstick*,Concurrency::non_greedy> j(2);
77 m_LeftChopstickProvider->link_target(&j);
78 m_RightChopstickProvider->link_target(&j);
79
80 //젓가락 한쌍을 집습니다.
81 return Concurrency::receive (j);
82 }
젓가락을 내려놓은 것은 간단합니다. 비동기 메시지 전송 함수인 Concurrency::asend()를 사용하여 젓가락이 이용가능함을 알리면 끝입니다.
83 void PutDownChopsticks(std::vector<Chopstick*>& v)
84 {
85 Concurrency::asend(m_LeftChopstickProvider,v[0]);
86 Concurrency::asend(m_RightChopstickProvider,v[1]);
87 }
마지막으로 철학자들과 젓가락, 젓가락제공자를 가지고 이들 모두를 셋업하는 역할을 하는 Table 클래스입니다. 주석을 참고하시면 쉽게 이해하실 수 있을 겁니다.
100 template<class PhilosopherList>
101 class Table
102 {
103 PhilosopherList & m_Philosophers;
104 std::vector<ChopstickProvider*> m_ChopstickProviders;
105 std::vector<Chopstick*> m_Chopsticks;
106
107 //이 생성자는 Table 클래서에서 유일한 public 메소드로 vector 변수들을 초기화하고 각 철학자에게 젓가락제공자를 할당합니다:
108 public:
109 Table(PhilosopherList& philosophers): m_Philosophers(philosophers)
110 {
111 //젓가락 및 젓가락제공자 vector를 채웁니다
112 for(size_t i = 0; i < m_Philosophers.size();++i)
113 {
114 m_ChopstickProviders.push_back(new ChopstickProvider());
115 m_Chopsticks.push_back(new Chopstick("chopstick"));
116 //젓가락제공자에 젓가락을 놓습니다
117 send(m_ChopstickProviders[i],m_Chopsticks[i]);
118 }
119 //철학자들을 식탁 자리에 앉힙니다
120 for(size_t leftIndex = 0; leftIndex < m_Philosophers.size();++leftIndex)
121 {
122 //rightIndex 계산
123 size_t rightIndex = (leftIndex+1)% m_Philosophers.size();
124
125 //왼쪽,오른쪽 제공자를 해당 철학자에 부여합니다
126 Concurrency::asend(& m_Philosophers[leftIndex].LeftChopstickProviderBuffer,
127 m_ChopstickProviders[leftIndex]);
128 Concurrency::asend(& m_Philosophers[leftIndex].RightChopstickProviderBuffer,
129 m_ChopstickProviders[rightIndex]);
130 }
131 }
132 ~Table(){
133 m_ChopstickProviders.clear();
134 m_Chopsticks.clear();
135 }
136
137 };
드디어 대망의 main() 함수입니다. 상태표시를 위한 call 블록과 C++0x 람다의 사용 이외에는, 전술할 클래스들을 사용하고 있을 뿐입니다.
206 int main()
207 {
208 //tr1 array를 사용해 철학자들을 생성합니다
209 std::tr1::array<Philosopher,5> philosophers = {"Socrates", "Descartes", "Nietzche", "Sartre", "Amdahl"};
210 Table<std::tr1::array<Philosopher,5>> Table(philosophers);
211 //상태표시에 이용할 call 블록들의 목록을 생성합니다
212 std::vector<Concurrency::call<PhilosopherState>*> displays;
213 //철학자 에이전트를 구동하고 상태표시 항목을 생성합니다
214 std::for_each(philosophers.begin(),philosophers.end(),[&displays](Philosopher& cur)
215 {
216 //상태표시용 call 블록을 하나 만듭니다
217 Concurrency::call<PhilosopherState>* consoleDisplayBlock = new Concurrency::call<PhilosopherState>([&](PhilosopherState in){
218 //cout은 각 출력 사이의 스레드안정성을 보장하지 않습니다
219 if(in == Eating)
220 std::cout << cur.m_Name << " is eating\n";
221 else
222 std::cout << cur.m_Name << " is thinking\n";
223 });
224 //상태표시 블록을 연결하고 벡터에 저장해둡니다
225 cur.CurrentState.link_target(consoleDisplayBlock);
226 displays.push_back(consoleDisplayBlock);
227 //그리고 에이전트를 구동합니다
228 cur.start();
229 });
230 //모두 완료되기를 대기
231 std::for_each(philosophers.begin(),philosophers.end(),[](Philosopher& cur)
232 {
233 cur.wait(&cur);
234 });
235
236 displays.clear();
237 };
이상을 실행하면 다음과 유사한 결과를 확인하실 수 있습니다.
주석에도 나와있듯이 스레드에 안전하지 않은 cout 출력으로 가끔 상태 메시지가 섞여였음을 확인할 수 있습니다. 그것 이외에는 철학자들이 사이좋게 식사를 하고 있음을 알 수 있습니다.
이렇듯 AAL을 사용하면 저수준의 스레드 함수나 동기화 개체들을 직접 다루지 않고도 쉽게 병렬 수행 작업을 작성할 수 있습니다. 병렬화에 고민하지 않고, 해당 응용프로그램의 도메인 문제에만 집중할 수 있는 것이죠.
이상입니다. 이제 새로운 로고와 함께 VS2010의 베타2도 나왔으니, 새로운 주제로 다시 찾아뵙지요. ^^
'VC++ 10 Concurrency Runtime' 카테고리의 다른 글
Parallel Patterns Library(PPL) - task group에서의 병렬 작업 취소 - 1 (0) | 2009.11.10 |
---|---|
Parallel Patterns Library(PPL) - combinable (0) | 2009.10.28 |
Parallel Patterns Library(PPL) - parallel_invoke (0) | 2009.10.20 |
Asynchronous Agents Library로 Dining Philosophers 문제 해결하기 - 2 (0) | 2009.09.17 |
Parallel Patterns Library(PPL) - parallel_for_each 알고리즘 (0) | 2009.09.12 |