Smalltalk80LanguageImplementationKor:Chapter 22

From 흡혈양파의 번역工房
Jump to: navigation, search
제 22 장 이벤트 주도 시뮬레이션

이벤트 주도 시뮬레이션

시뮬레이션은 실제 세계나 환상의 세계에서 객체의 시스템에 대한 표현이다. 컴퓨터 시뮬레이션을 생성하는 목적은 시뮬레이트된 상황을 이해, 가령 대기줄의 행동, 직원의 업무량, 또는 고객에 대한 서비스의 시간표를 이해하기 위한 프레임워크를 제공하기 위함이다. 특정 유형의 시뮬레이션을 "counter simulations"(카운터 시뮬레이션)이라고 부른다. 이는 점원이 작업하는 카운터 또는 장소가 있는 상황을 나타낸다. 점원이 자리에 있지 않으면 고객은 대기줄에 들어선다. 대기줄의 첫 번째 손님은 자리에 있는 다음 점원에게 서비스를 받는다. 주어진 시뮬레이션에는 보통 여러 종류의 장소와 여러 명의 손님이 있으며, 각 손님마다 장소를 방문하는 목적을 갖고 있다. 그러한 상황을 보여주는 예는 은행, 세차장, 미용실, 병원, 음식점, 공항, 우체국, 놀이공원, 공장 등 많다. 컴퓨터 시뮬레이션은 이러한 상황에 대한 통계를 수집하고 그 조직에 대한 새로운 생각들을 시험할 수 있도록 해준다.


카운터 시뮬레이션에 참여하는 객체들은 다소 독립적으로 작동한다. 따라서 시뮬레이트된 다양한 객체들의 활동을 조정하거나 동기화하는 문제를 고려해야 한다. 보통은 메시지 전달 메커니즘을 통해 행동을 조정한다. 하지만 일부 객체들은 특정 중요한 순간에 자신들이 행동을 동기화해야 하고, 어떤 객체들은 주어진 순간에 이용할 수 없는 특정 자원으로 접근하지 않고서는 바람직한 행동을 수행하지 못하기도 한다. Smalltalk-80 시스템 클래스, Process, Semaphore, SharedQueue는 독립된 활동을 동기화하는 기능을 제공한다. 카운터 시뮬레이션의 일반적인 설명을 지원하기 위해서는 아래 요소의 조정을 위한 메커니즘이 필요하다.

  • 고정된 크기의 자원 사용,
  • 변동 자원의 사용,
  • 두 객체의 행동에 동시성 표출.


고정된 자원이란 소모 또는 소모 불가 자원이 될 수 있다. 가령, 젤리빈은 소모성이고 과자점에서 고정된 자원에 해당하며, 책은 도서관의 소모 불가 자원에 해당한다. 변동 자원은 보통 재생 가능하거나 생산자/소비자가 동기화되어 있다. 매장에서는 젤리 빈의 공급을 변동 자원으로 모델화할 수 있는데, 공급을 재갱신할 수 있기 때문이다. 재생 가능하면서 소모 불가 자원도 생각해낼 수 있다. 그러한 자원은 자동차 대여 시뮬레이션에서 모델화될 수 있는데, 새로운 자동차가 제작되어 대여 자동차 공급수량에 추가되므로 자동차는 재생 가능한 자원이라 할 수 있지만, 동시에 대여한 자동차는 다음 손님이 사용하도록 딜러에게 반환되어야 하므로 소모 불가 자원이기도 하다. 사실상 대부분의 소모 불가 자원은 소모가 가능한데, 가령 도서관 책은 계속해서 대여하다보면 결국 낡게 되고, 대여 자동차는 결국 망가지기 때문이다. “소모 불가”란 말은 적어도 시뮬레이션의 관심 기간 동안에 소모되지 않는 것을 의미한다.


시뮬레이션에서 두 객체의 행동을 동기화하여 과제를 함께 수행하는 모습을 보여야 하는 경우, 두 객체는 서버/클라이언트 관계에 있다고 말한다. 예를 들자면, 의사가 진찰을 하기 위해서는 환자의 협조가 필요하다. 서버는 조정되는 자원으로, 한 명 또는 그 이상의 고객이 자원을 요청할 때만 작업을 실행할 수 있는 시뮬레이션 객체이다.


시뮬레이션에서 중요한 측면으로는 시간이 지나면서 변하는 상황을 모델화하는 것을 들 수 있는데, 손님이 은행에 들어오는 모습과 나가는 모습, 자동차가 세차장에 들어와 세차하고 건조시킨 후 떠나는 모습, 항공기가 착륙하고 승객들을 하차시킨 후 또 승객들을 싣고 공항에서 이륙하는 모습을 예로 들 수 있겠다. 이러한 활동은 시간과 관련된 것이 보통인데, 특정 시간이나 특정 시간 간격 이내에 이벤트가 발생한다. 따라서 행동은 특정 시간 개념과 동기화되어야 한다. 보통은 이러한 시간 개념 자체가 시뮬레이트된다.


시뮬레이트된 객체를 실제 시간이나 시뮬레이트된 시간과 관련시켜 표현하는 방법에는 여러 가지가 있다. 그 중 한 가지는 시간이 보통대로 흘러가는 방식이다. 시계가 째깍거릴 때마다 모든 객체에는 바람직한 행동을 취할 기회가 주어진다. 시계는 시뮬레이션을 위한 동기화 장치의 역할을 하여 parallelism의 모습을 제공할 기회를 제공하는데, 시계는 주어진 시간에 적절한 모든 행동이 완료될 때까지 기다리기 때문이다. 때로는 주어진 시간에 어떤 행동도 취하지 않을 것이다.


또 다른 방법으로, 시계를 다음 행동이 발생하게 될 시간에 따라 앞으로 이동하는 수가 있다. 이런 경우, 시스템은 다음에 발생하도록 계획된 이산적 행동 또는 이벤트에 의해 구동된다. 이러한 접근법을 이용한 시뮬레이션의 구동은 시뮬레이트된 시간으로 정렬된 이벤트의 큐의 유지에 따라 좌우된다. 이벤트가 완료될 때마다 큐에서 다음 이벤트를 취하고 시계는 지정된 시간으로 이동한다.


본장에 제시된 시뮬레이션은 "event-driven"(이벤트 주도) 접근법을 기반으로 한다. 이는 독립된 객체의 컬렉션이 존재하는 시뮬레이션을 포함하는데, 각 객체마다 해야 할 작업의 집합이 있고 (확보해야 하는 서비스 또는 자원), 각 객체는 시뮬레이트된 상황에서 다른 객체와 활동 시간을 조정할 필요가 있다.


본장에서는 그러한 시뮬레이션을 발달시킬 수 있는 프레임워크를 설명한다. SimulationObject 클래스는 시뮬레이션에서 나타날 수 있는 일반적인 유형의 객체, 즉 해야할 작업 집합이 있는 객체를 설명한다. 클래스의 메시지 프로토콜은 작업이 실행되는 프레임워크를 제공한다. Simulation 클래스의 인스턴스는 시뮬레이트된 시계와 이벤트의 큐를 유지한다. 시스템에 새로운 객체가 (에: 손님) 도달하는 데 대한 명세와 자원에 대한 (예: 점원) 명세는 이 클래스에서 조정된다.


다음 23장에서는 시뮬레이션을 실행하면서 생성된 데이터를 수집하는 방법을 다룬다. 통계 수집은 Simulation 클래스와 (또는) SimulationObject 클래스의 서브클래스에 일반 메커니즘을 제공함으로써 처리할 수 있다. 아니면 각 시뮬레이션 예제가 그 행동에 관한 정보를 수집하는 고유의 메커니즘을 제공할 수도 있다.


24장에서는 두 가지 동기화 유형을 사용하고, 고정된 자원의 사용과 변동 자원의 사용을 공유하는 시뮬레이션 예제를 설명하고, 25장에서는 두 가지 시뮬레이션 객체, 즉 서비스를 원하는 객체와 서비스를 제공하는 객체 간 조정에 대한 추가 지원을 소개하겠다.


시뮬레이션을 위한 프레임워크

이번 절에는 SimulationObject와 Simulation 클래스에 기본 프로토콜을 제공하는 클래스의 설명이 포함되어 있다. 이 클래스들은 두 번씩 제시된다. 첫 번째는 디폴트(default) 예제를 어떻게 생성하는지에 대한 설명과 함께 프로토콜의 설명을 제공하고, 두 번째는 이러한 클래스들의 구현을 제공한다.

시뮬레이션 객체

세차장을 시뮬레이트한다고 가정하자. 세차장의 주요 구성요소는 세차 장소, 건조 장소, 계산대, 세차직원, 건조직원, 계산원, 그리고 트럭이나 승용차와 같은 여러 종류의 자동차가 있겠다. 이러한 요소는 행위에 따라 분류할 수 있다. 주요 분류는 직원들이 위치하고 작업이 이루어지는 장소, 세차직원과 건조직원 그리고 계산원이 포함된 노동자, 장소 내에서 손님에 해당하는 자동차로 이루어진다. 이러한 분류는 Smalltalk 객체의 세 가지 클래스, Place, Worker, Customer로 해석할 수 있겠다. 하지만 객체의 이러한 클래스는 각각 할 일이 있는 객체를 설명한다는 점에서 비슷한데, 가령 Customer는 서비스를 요청하고 Worker는 서비스를 제공하며 Place는 자원을 제공한다. 특히 Place는 직원이 처리할 수 있는 양보다 손님의 수가 많을 경우 대기 큐(waiting queue)를 제공한다. 이러한 유사점은 시뮬레이트된 상황에서 나타나는 객체를 설명하기 위해 SimulationObject 슈퍼클래스에서 모델화되는데, SimulationObject는 해야 할 작업 순서를 제공할 수 있는 객체라면 무엇이든 해당한다. 각 객체는 객체가 시뮬레이션으로 들어갈 때 시작되는 활동의 주요 순서를 정의한다. 가령, 세차장에서 자동차의 활동은 세차직원에게 세차를 요청하는 일, 세차하는 동안 기다리는 일, 건조직원에게 요청하는 일, 건조되는 동안 기다리는 일, 서비스에 대한 대가를 지불하는 일, 마지막으로 세차장을 떠나는 일까지 포함된다.


SimulationObject 클래스는 객체가 들어오고 그 작업을 실행한 후 시뮬레이션을 벗어나기까지 일반적인 제어 순서를 명시한다. 이러한 순서는 객체에게 startUp, tasks, finishUp 메시지를 전송하는 것으로 구성된다. 기술(descriptive) 변수의 초기화는 initialize 메시지에 대한 응답으로 명시된다. 이러한 메시지들은 startUp과 연관된 메서드에 의해 호출된다. tasks와 initialize 메시지에 대한 응답은 SimulationObject의 서브클래스가 구현한다.

초기화(initialization)
initialize 인스턴스 변수가 있을 경우 초기화한다.
simulation control startUp 인스턴스 변수를 초기화한다. 수신자가 시뮬레이션에 들어가고 있음을 시뮬레이션으로 알린 후 수신자의 작업을 시작한다.
tasks 수신자가 실행해야 하는 활동의 순서를 정의한다.
finishUp 수신자의 작업이 완료되었다. 시뮬레이션에게 알린다.
SimulationObject 인스턴스 프로토콜


어떤 SimulationObject든 작업을 설명할 때 사용할 수 있는 메시지가 몇 가지 있다. 그 중 하나는 holdFor: aTimeDelay인데, 여기서 인자 aTimeDelay는 객체가 시뮬레이트된 시간을 기준으로 액션을 지연시킨 시간량에 해당한다. 이러한 지연은 짐작컨대 객체가 어떤 활동을 실행하는 기간을 생성하기 위함이다.


이러한 범주의 메시지, 모델러의 task language를 호출하는 이유는 tasks 메시지 구현의 일부로 SimulationObject로 전송된 메시지임을 나타내기 위함이다.


시뮬레이션은 "젤리 빈”과 같이 시뮬레이션 객체로 얻을 수 있는 간단한 또는 정적인 자원을 포함할 수 있다. 아니면 시뮬레이션은 "coordinated"(조정된) 자원, 즉 다른 시뮬레이션 객체의 작업과 동기화되어야 하는 시뮬레이션 객체로 구성되기도 한다. 작업 언어는 자원을 얻거나 제공하기 위해 각 자원 유형으로 접근하는 메시지를 포함한다.


정적 자원에 대한 메시지는 세 가지 종류가 있다. resourceName이라는 자원의 amount를 얻기 위한 메시지 두 가지는 다음과 같다.

acquire: amount ofResource: resourceName
acquire: amount ofResource: resourceName withPriority: priorityInteger


resourceName이라는 자원의 amount를 제공하는 메시지는 다음과 같고,

produce: amount ofResource: resourceName


얻은 정적 자원을 포기하는 메시지는 다음과 같다.

release: aStaticResource


조정된 자원에 사용할 수 있는 메시지도 세 가지 종류가 있다. resourceName라는 자원을 얻기 위한 메시지는 (여기서 자원은 손님의 유형을 모델화하는 SimulationObject이고, 질문자는 점원과 같은 종업원에 해당한다) 아래와 같다.

acquireResource: resourceName


resourceName이라는 자원을 생성하기 위한 메시지는 아래와 같고 (질문자는 손님),

produceResource: resourceName


획득한 자원(이제 작업 이벤트를 재개할 수 있는 SimulationObject)을 포기하는 메시지는 다음과 같다.

resume: anEvent


SimulationObject가 정적 자원 요청(acquire:ofResource: 또는 request:)을 할 때는 요청의 중요성 수준을 나타냄으로써 요청을 한다. 숫자 0은 중요성이 가장 낮은 요청이고, 숫자가 커질수록 중요성도 높아진다. acquire:ofResource: 메시지는 0의 우선순위 수준을 가정하고, acquire:ofResource:withPriority: 는 세 번째 인자에서 특정 수준을 명시한다.


두 개의 쿼리는 정적 자원이 시뮬레이션에 있는지, 그리고 얼마나 많은 자원이 이용 가능한지 확인한다. 이는 바로 시뮬레이션이 String이 참조하는 자원, resourceName: 을 가지는지 여부를 응답하는 resourceAvailable:resourceName과, 적어도 남은 자원의 amount가 존재하는지 여부를 응답하는 inquireFor: amountofResource: resourceName이다.


SimulationObject가 그 작업을 다른 SimulationObject의 작업과 동기화할 때는 그러한 객체의 이용 가능성 유무를 아는 것이 유용하겠다. 조정된 작업의 요청자 또는 제공자가 이용 가능한지 알아내도록 지원하는 추가 질의 메시지가 두 개 있는데 바로 numberOfProvidersOfResources: resourceName과 numberOfRequestersOfResource: resourceName이다.


또한 SimulationObject에 전송되는 메시지는 Simulation에게 실행 중지(stop running)를 요청할 수 있다. 이는 stopSimulation 메시지에 해당한다.

작업 언어(task language)
initialize 인스턴스 변수가 있다면 초기화하라.
holdFor: aTimeDelay 시뮬레이트된 시간의 aTimeDelay 시간을 통과할 때까지 수신자의 다음 작업 실행을 지연시킨다.
acquire: amount ofResource: resourceName 시뮬레이션에게 resourceName이라는 String이 참조하는 간단한 자원 resourceName을 제공하도록 요청한다. 존재할 경우 자원의 amount를 수신자에게 제공할 것을 요청한다. 존재하지 않을 경우 시뮬레이션 사용자(프로그래머)에게 오류가 발생하였음을 알린다.
acquire: amount ofResource: resourceName withPriority: priorityNumber 시뮬레이션에게 resourceName이라는 String이 참조하는 간단한 자원을 제공하도록 요청한다. 존재할 경우 자원의 amount를 수신자에게 제공할 것을 요청하고, 자원을 얻기 위한 우선순위가 priorityNumber로 설정되도록 고려한다. 존재하지 않을 경우 시뮬레이션 사용자(프로그래머)에게 오류가 발생하였음을 알린다.
produce: amount ofResource: resourceName 시뮬레이션에게 resourceName이라는 String이 참조하는 간단한 자원을 제공하도록 요청한다. 존재할 경우 자원의 amount만큼 더한다. 존재하지 않을 경우 생성한다.
release: aStaticResource 수신자가 인자 aStaticResource가 참조하는 자원을 사용해왔다. 더 이상 필요로 하지 않으며 재활용이 가능하다.
inquireFor: amount ofResource: resourceName 시뮬레이션이 최소한 resourceName이라는 String이 참조하는 자원의 수량 amount를 갖고 있는지 응답한다.
resourceAvailable: resourceName 시뮬레이션이 resourceName이라는 String이 참조하는 자원을 갖고 있는지 응답한다.
acquireResource: resourceName 시뮬레이션에게 resourceName이라는 String이 참조하는 자원 시뮬레이션 객체를 제공하도록 요청한다. 존재할 경우 수신자에게 그 서비스를 제공하도록 요청한다. 존재하지 않을 경우 시뮬레이션 사용자(프로그래머)에게 오류가 발생하였음을 알린다.
produceResource: resourceName 수신자가 resourceName이라는 String이 참조하는 자원으로서 행동하도록 한다. 이 자원에게 서비스를 제공하는 (획득하는) 또 다른 SimulationObject를 기다린다.
resume: anEvent 수신자가 인자 anEvent가 참조하는 자원으로 서비스를 제공해왔다. 서비스가 완료되어 자원 aSimulationObject는 그 작업을 계속할 수 있다.
numberOfProvidersOfResource: resourceName resourceName이라는 String이 참조하는 자원으로서 행동하여 작업을 조정하길 기다리는 SimulationObject 개수를 응답한다.
numberOfRequestersOfResource: resourceName resourceName이라는 String이 참조하는 자원을 획득함으로써 그 작업을 조정하길 기다리는 SimulationObject 개수를 응답한다.
stopSimulation 수신자가 실행을 중단하고 있음을 시뮬레이션에게 알린다. 모든 계획된 이벤트가 제거되고 시뮬레이션에서 더 이상 어떤 일도 발생하지 않는다.
SimulationObject 인스턴스 프로토콜


다음 장에서부터 제시할 예제들은 모델러의 작업 언어에서 각 메시지를 설명한다.


시뮬레이션(Simulations)

Simulation 클래스의 목적은 시뮬레이션 객체의 토폴로지를 관리하고, 시뮬레이트된 시간에 따라 발생할 액션을 계획하는 데에 있다. Simulation 클래스의 인스턴스들은 SimulationObjects의 컬렉션, 현재 시뮬레이트된 시간, 호출을 기다리는 이벤트 큐에 대한 참조를 유지한다.


시뮬레이션에 적절한 시간 단위는 인스턴스 변수에 저장되고 부동소수점 수로 표현된다. 단위는 밀리초, 분, 일(day) 등이 가능하다. 시뮬레이션은 다음 이벤트가 언제 발생하기로 계획되어 있는지 결정하기 위해 큐를 확인하고, 인스턴스 변수를 다음 이벤트와 연관된 시간으로 설정함으로써 시간을 앞당긴다(advance). 이벤트의 큐가 비어 있다면 시뮬레이션이 종료된다.


시뮬레이션 객체들은 다음과 같이 여러 스케줄링(scheduling) 메시지들 중 하나에 대한 응답으로 시뮬레이션에 들어간다.

scheduleArrivalOf: aSimulationObjectClass
    accordingTo: aProbabilityDistribution or
scheduleArrivalOf: aSimulationObject at: aTimeInteger.


이러한 메시지들은 defineArrivalSchedule 메시지에 대한 응답으로 시뮬레이션이 처음 초기화될 때, 또는 SimulationObject가 실행되는 작업 순서의 일부로서 시뮬레이션으로 전송된다. 첫 번째 메시지의 두 번째 인자 aProbabilityDistribution은 21장에 정의한 확률 분포의 예다. 이번 장에서는 21장에 제공된 정의를 이용할 수 있는 것으로 가정한다. 확률 분포는 첫 번째 인자의 인스턴스 aSimulationObjectClass가 생성되고 그곳으로 startUp 메시지가 전송되는 간격을 정의한다.


뿐만 아니라 Simulation은 특정 액션의 순서를 계획하는 것과 관련된 메시지도 지원한다. 이러한 메시지로는 schedule: actionBlock at: timeInteger 그리고 schedule: actionBlock after: amountOfTime이 있다.


시뮬레이션에서 자원을 정의하기 위해서는 모델러가 가능한 메시지 두 개 중에서 하나 또는 두 개를 시뮬레이션으로 전송할 수 있어야 한다. 첫 번째는 아래와 같으며,

self produce: amount of: resourceName


여기서 두 번째 인자 resourceName은 시뮬레이션에서 수량화할 수 있는 간단한 자원을 명명하는 String이고, 첫 번째 인자는 앞으로 이용할 수 있는 이 자원의 (추가) 수량이다.

self coordinate: resourceName


두 번째는 위의 메시지로, String은 인자 resourceName이 시뮬레이션에서 특정 객체가 제공하게 될, 그리고 다른 객체들이 요청하게 될 자원을 명명한다.

초기화(initialization)
initialize 수신자의 인스턴스 변수를 초기화한다.
모델러의 초기화 언어(modeler's initialization language)
defineArrivalSchedule 주로 확률 분포 함수를 기반으로 명시된 시간 간격에서 시뮬레이션으로 들어가도록 시뮬레이션 객체를 계획한다. 이 메서드는 서브클래스에 의해 구현된다. 이는 수신자로 전송되는 아래 형태의 메시지 시퀀스를 수반한다.
schedule:at:, scheduleArrivalOf:at:,
scheduleArrivalOf:accordingTo:, 또는
scheduleArrivalOf:accordingTo:startingAt:.
이러한 메시지들의 설명은 메시지의 다음 범주를 참고한다.
defineResources 시뮬레이션으로 처음 들어가는 자원을 명시한다. 이는 주로 획득해야 할 자원의 역할을 한다. 이 메서드는 서브클래스에 의해 구현되고 수신자로 (예: self로) 전송되는 produce: amount of: resourceName 형태의 메시지 시퀀스를 수반한다.
모델러의 작업 언어(modeler's task language)
produce: amount of: resourcename resourceName이라는 String이 참조하는 자원의 amount에 대한 추가 수량은 수신자의 일부가 된다. 자원이 수신자 내에 더 이상 존재하지 않을 경우 추가하고, 이미 존재할 경우 이용 가능한 수량을 증가시킨다.
coordinate: resourceName resourceName이라는 String이 참조하는 자원의 사용이 수신자에 의해 조정될 예정이다.
schedule: actionBlock after: timeDelayInteger 시뮬레이트된 시간량 timeDelayInteger가 지나고 나서 평가될 프로그램 actionBlock을 설정한다.
schedule: actionBlock at: timeInteger 특정 시뮬레이트된 시간 timeInteger에서 발생할 액션의 시퀀스를 (actionBlock) 계획한다.
scheduleArrivalOf: aSimulationObject at: timeInteger 시뮬레이션 객체 aSimulationObject가 명시된 시간 timeInteger에 시뮬레이션으로 들어가도록 계획한다.
scheduleArrivalOf: aSimulationObjectClass
accordingTo: aProbabilityDistribution
확률 분포 aProbabilityDistribution을 기반으로 aSimulationObjectClass의 인스턴스인 시뮬레이션 객체가 명시된 시간 간격으로 시뮬레이션에 들어가도록 계획한다. 첫 번째 인스턴스는 지금 들어가도록 계획한다. 가능한 확률 분포의 정의는 21장을 참고한다.
scheduleArrivalOf: aSimulationObjectClass
accordingTo: aProbabilityDistribution
startingAt: timeInteger
확률 분포 aProbabilityDistribution을 기반으로 aSimulationObjectClass의 인스턴스인 시뮬레이션 객체가 명시된 시간 간격으로 시뮬레이션에 들어가도록 계획한다. 첫 번째 인스턴스는 timeInteger 시간에 들어가도록 계획한다.
Simulation 인스턴스 프로토콜


위의 스케줄링 메시지에서 scheduleArrivalOf:at: 은 첫 번째 인자로 SimulationObject 인스턴스를 취하는 반면 scheduleArrivalOf:accordingTo:은 SimulationObject 클래스를 취한다. 이러한 메시지는 서로 다르게 사용되는데, 첫 번째는 SimulationObject가 스스로를 재스케줄링하는 데 사용하는 반면, 두 번째 메시지는 SimulationObjects가 시스템으로 도착하는 것을 시작하는 데 사용된다.


Simulation에 대한 프로토콜은 몇 가지 접근 메시지를 포함한다. 그 중 하나인 includesResouceFor: resourceName은 주어진 이름을 가진 자원이 시뮬레이션에 존재하는지 여부를 결정하기 위해 SimulationObject에 의해 전송된다.

접근하기(accessing)
includesResourceFor: resourceName resourceName이라는 String이 참조하는 자원을 수신자가 갖고 있는지 응답한다. 그러한 자원이 존재하지 않으면 오류를 보고한다.
provideResourceFor: resourceName resourceName이라는 String이 참조하는 자원을 응답한다.
time 수신자의 현재 시간을 응답한다.
Simulation 인스턴스 프로토콜


시뮬레이션 제어 프레임워크는 SimulationObject 클래스의 것과 비슷하다. Simulation을 생성하여 그곳으로 startUp 메시지를 전송하면 초기화가 처리된다. 시뮬레이션 객체와 새로운 객체의 스케줄링은 이벤트를 생성하여 이벤트 큐에 위치된다. Simulation은 우선 초기화가 되고 나면 큐에 더 이상 이벤트가 없을 때까지 proceed 메시지를 전송하여 실행되도록 만들어졌다.


시뮬레이션을 실행하는 과정에서 객체는 들어가고 나갈 것이다. 시뮬레이션 객체를 스케줄링하기 위한 프로토콜의 일부로, 객체는 그 시뮬레이션에게 자신이 들어가거나 나오고 있음을 알린다. 그에 해당하는 메시지는 enter: anObject와 exit: anObject이다. 이러한 메시지에 대한 응답에서 시뮬레이션 객체가 시뮬레이션으로 들어가고 나오는 데 대한 통계를 수집할 수 있다. 아니면 서브클래스가 시뮬레이션으로의 객체 진입을 거부하거나, 서브클래스가 시뮬레이션을 떠나도록 내버려두는 대신 객체의 재스케줄링을 선택할 수도 있다.

시뮬레이션 제어(simulation control)
startUp 처음 시뮬레이션 객체와 새로운 객체의 도착 스케줄을 명시한다.
proceed 단일 이벤트 실행이다. 큐의 첫 번째 이벤트가 있다면 그것이 제거되고 시간이 이벤트의 시간으로 업데이트되며 이벤트가 시작된다.
finishUp 나머지 시뮬레이션 객체가 있다면 그에 대한 참조를 해제한다.
enter: anObject 인자 anObject가 들어가고 있음을 수신자에게 알린다.
exit: anObject 인자 anObject가 나오고 있음을 수신자에게 알린다.
Simulation 인스턴스 프로토콜


위의 메시지 중 Simulation 클래스 내에서 기본 응답은 거의 아무 일도 하지 않는 것이다. 특히 enter: 과 exit: 메시지에 대해서는 아무 일도 하지 않는다. defineArrivalSchedule과 defineResources 메시지 또한 아무 일도 하지 않는다. 그 결과 startUp 메시지는 얻는 것이 없다. 이러한 메시지들은 서브클래스가 사용할 프레임워크를 제공하고, 시뮬레이션 특정적 행위를 추가하기 위해 이러한 메시지를 오버라이드하는 서브클래스가 생성된다.


"Default" 예제: NothingAtAll

앞의 여러 장에서 소개한 많은 시스템 클래스 예제와 달리 Simulation과 SimulationObject 슈퍼클래스는 주로 기본 메시지를 다음으로 구현하지 않는다.

self subclassResponsibility


그에 따라 이러한 클래스의 인스턴스를 성공적으로 생성할 수 있다. 이러한 인스턴스들은 후에 skeletal 예제의 역할을 하는 기본 또는 "default"(디폴트) 시뮬레이션의 일부로 사용 가능하다. 앞서 살펴보았듯 그러한 시뮬레이션 객체는 아무 일도 하지 않도록 계획할 수 있고 어떠한 이벤트도 포함하지 않도록 구성할 수 있다. 이러한 디폴트를 조금씩 조정하면 상당히 많은 시뮬레이션의 발전이 가능하다. 실행 중인 디폴트 예제를 이용해 디자이너/프로그래머는 시뮬레이션을 점차적으로 수정 및 검사하여 흥미롭지 않은 슈퍼클래스의 인스턴스를 적절한 서브클래스의 인스턴스로 대체할 수 있다. 시뮬레이션 예제 NothingAtAll이 이러한 “디폴트” 시뮬레이션 개념을 설명한다.


NothingAtAll 클래스를 Simulation의 서브클래스로 선언하는 일 외에는 말 그대로 아무 것도 하지 않는다고 가정하자. NothingAtAll은 defineResources 메시지에 대한 응답으로 아무 일도 하지 않기 때문에 첫 자원을 갖고 있지 않다. 그리고 defineArrivalSchedule 메시지에 대한 응답으로 아무 일도 하지 않으므로 다양한 간격으로 도착하는 시뮬레이션 객체가 없다. 이제 다음과 같은 문을 실행한다.

NothingAtAll new startUp proceed


문을 실행하면 NothingAtAll의 인스턴스가 생성되고 startUp 메시지가 전송된다. 이는 어떤 자원이나 객체도 계획되지 않은 시뮬레이션이므로 이벤트 큐 또한 비어있다. proceed 메시지에 대한 응답으로, 시뮬레이션은 큐가 비어 있고 아무 일도 하지 않는다고 결정한다.


NothingAtAll의 설명을 수정하여 defineArrivalSchedule 메시지에 대한 응답을 명시한다. 여기서 도착하기로 계획된 객체는 DoNothing 클래스의 인스턴스들이다. DoNothing은 단순히 SimulationObject의 서브클래스로서 생성된다. DoNothing은 실행해야 할 작업을 갖고 있지 않으므로 시뮬레이션으로 들어가자마자 나오게 된다.

클래스명 DoNothing
슈퍼클래스 SimulationObject
인스턴스 메서드
no new methods
클래스명 NothingAtAll
슈퍼클래스 Simulation
인스턴스 메서드
initialization
    defineArrivalSchedule
        self scheduleArrivalOf: DoNothing
            accordingTo: (Uniform from: 1 to: 5)


이러한 NothingAtAll 버전은 빈 공간을 들어갔다가 시간을 전혀 들이지 않고 둘러본 다음 나오는 일련의 방문자를 나타낸다. 이번 장에 실린 예제에서 확률 분포 Uniform은 21장에 명시된 것으로 간주한다. 위의 명세에 따르면 DoNothing 클래스의 새로운 인스턴스는 0부터 시작해 시뮬레이트된 시간의 1부터 5단위 시간마다 시뮬레이션에 도달해야 한다. 아래 표현식을 평가하면 시뮬레이션이 생성되어 startUp 메시지가 전송된 후 다시 proceed 메시지가 반복하여 전송된다.

aSimulation  NothingAtAll new startUp.
[aSimulation proceed] whileTrue


startUp 메시지는 DoNothing의 인스턴스를 계획하는 defineArrivalSchedule 메시지를 호출한다. proceed 메시지가 시뮬레이션으로 전송될 때마다 DoNothing이 들어가거나 나간다. 평가 결과 다음과 같은 이벤트 시퀀스가 야기될 수 있다. 각 이벤트의 시간이 좌측에 표시되고 이벤트의 설명은 우측에 표시되어

0.0 a DoNothing enters
0.0 a DoNothing exits
3.21 a DoNothing enters
3.21 a DoNothing exits
7.76 a DoNothing enters
7.76 a DoNothing exits


이런 식으로 계속된다.


이제 해야 할 작업이 있는 시뮬레이션 객체의 더 많은 유형의 도착을 계획하여 시뮬레이션을 더 재밌게 만들 수 있다.


표현식 next를 평가하여 결정된 무작위 시간량, 즉 4에서 10 까지 시뮬레이트된 시간동안 빈 방으로 들어가 둘러보는 작업을 수행하는 SimulationObject로 Visitor를 정의한다.

클래스명 Visitor
슈퍼클래스 SimulationObject
인스턴스 메서드
simulation control
    tasks
        self holdFor: (Uniform from: 4.0 to: 10.0) next


이제 NothingAtAll은 다음과 같이 정의된다.

클래스명 NothingAtAll
슈퍼클래스 Simulation
인스턴스 메서드
initialization
    defineArrivalSchedule
        self scheduleArrivalOf: DoNothing
            accordingTo: (Uniform from: 1 to: 5).
        self scheduleArrivalOf: Visitor
            accordingTo: (Uniform from: 4 to: 8)
            startingAt: 3


두 종류의 객체가 시뮬레이션으로 들어가는데, 하나는 둘러보는 데 시간을 들이지 않는 객체이고 (DoNothing) 나머지는 짧은 시간 동안 방문하는 객체이다 (Visitor). 아래를 실행할 경우,

aSimulation  NothingAtAll new startUp.
[aSimulation proceed] whileTrue


다음과 같은 이벤트 시퀀스를 야기할 수 있고,

0.0 a DoNothing enters
0.0 a DoNothing exits
3.0 a Visitor enters
3.21 a DoNothing enters
3.21 a DoNothing exits
7.76 a DoNothing enters
7.76 a DoNothing exits
8.23 a (the first) Visitor exits after 5.23 seconds


위와 같은 방식으로 진행된다.


시뮬레이션 클래스의 구현

지금까지 제공된 예제에서 이벤트의 시퀀스가 발생한 방식을 추적하기 위해서는 두 가지 클래스의 구현을 표시할 필요가 있다. 이러한 구현은 15장에 설명된 Smalltalk-80 시스템 내 다중 독립 프로세스의 제어를 설명한다.

SimulationObject 클래스

시스템에서 생성된 모든 SimulationObject는 그것이 기능하는 Simulation으로 접근할 필요가 있다. 가령, 객체가 들어가거나 나오고 있음을 시뮬레이션에게 알리는 메시지를 전송할 경우 그러한 접근은 꼭 필요하다. 그러한 접근을 지원하기 위해 SimulationObject는 ActiveSimulation이라는 클래스 변수를 갖고 있는데, 이는 인스턴스가 활성화될 때 (startUp 메시지가 전송됨) Simulation의 각 인스턴스에 의해 초기화된다. 이러한 접근법은 한 번에 하나의 Simulation만 활성화될 것이라고 가정한다. 즉, SimulationObject의 서브클래스에 대한 작업은 가령 현재 시간을 결정하기 위해 그 시뮬레이션으로 직접 메시지를 전송한다. SimulationObject는 어떠한 인스턴스 변수도 명시하지 않는다.

클래스명 Simulation Object
슈퍼클래스 Object
클래스 변수명 ActiveSimulation
클래스 메서드
class initialization
    activeSimulation: existingSimulation
        ActiveSimulation  existingSimulation
instance creation
    new
        super new initialize


종종 객체의 "life cycle" (생명 주기)이라고도 불리는 시뮬레이션 제어 프레임워크는 startUp-tasks-finishUp 시퀀스를 수반한다. SimulationObject가 먼저 시뮬레이션에 도달하면 그곳으로 startUp 메시지가 전송된다.

인스턴스 메서드
simulation control
    initialize
        "Do nothing. Subclasses will initialize instance variables."
        self
    startUp
        ActiveSimulation enter: self.
        "First tell the simulation that the receiver is beginning to do my tasks."
        self tasks.
        self finishUp
    tasks
        "Do nothing. Subclasses will schedule activies."
        self
    finishUp
        "Tell the simulation that the receiver is done with its tasks."
        ActiveSimulation exit: self


task language 범주는 모델러가 SimulationObject의 액션 시퀀스를 명시할 때 사용할 수 있는 메시지로 구성된다. 객체는 시뮬레이트된 시간(holdFor:)의 증가량을 보류할 수 있다. 객체는 자원의 역할을 하는 또 다른 시뮬레이션 객체로의 접근성을 얻기 위한 시도가 가능하다 (acquire:ofResource:). 뿐만 아니라 자원의 이용 가능성에 대한 결정을 내릴 수도 있다 (resourceAvailable).

task language
    holdFor: aTimeDelay
        ActiveSimulation delayFor: aTimeDelay
    acquire: amount ofResource: resourceName
        "Get the resource and then tell it to acquire amount of it. Answers an instance of StaticResource"
        (ActiveSimulation provideResourceFor: resourceName)
            acquire: amount
            withPriority: 0
    acquire: amount ofResource: resourceName withPriority: priority
        (ActiveSimulation provideResourceFor: resourceName)
            acquire: amount
            withPriority: priority
    produce: amount ofResource: resourceName
        ActiveSimulation produce: amount of: resourceName
    release: aStaticResource
        aStaticResource release
    inquireFor: amount ofResource: resourceName
        (ActiveSimulation provideResourceFor: resourceName)
            amountAvailable > = amount
    resourceAvailable: resourceName
        "Does the active simulation have a resource with this attribute available?"
        ActiveSimulation includesResourceFor: resourceName
    acquireResource: resourceName
        (ActiveSimulation provideResourceFor: resourceName)
            acquire
    produceResource: resourceName
        (ActiveSimulation provideResourceFor: resourceName)
    resume: anEvent
        anEvent resume
    numberOfProvidersOfResource: resourceName
        | resource |
        resource  ActiveSimulation provideResourceFor: resourceName.
        resource serversWaiting
            ifTrue: [resource queueLength]
            ifFalse: [0]
    numberOfRequestersOfResource: resourceName
        | resource |
        resource  ActiveSimulation provideResourceFor: resourceName.
        resource customersWaiting
            ifTrue: [resource queueLength]
            ifFalse: [0]
    stopSimulation
        ActiveSimulation finishUp


Simulation은 자원의 Set을 저장한다. 정적 자원의 경우, ResourceProvider 클래스의 인스턴스가 저장되고, 두 개 또는 그 이상의 시뮬레이션 객체 간에 조정된 작업으로 구성된 자원의 경우 ResourceCoordinator의 인스턴스가 저장된다.


SimulationObject가 정적 자원을 요청하고 (acquire:ofResource:) 그러한 요청이 성공하면 SimulationObject에 StaticResource 클래스의 인스턴스가 주어진다. StaticResource는 그것을 참조한 자원과 그것이 나타내는 자원의 수량을 참조한다. SimulationObject 클래스에 표시된 메서드를 감안하면 SimulationObject가 얻을 수 있는 자원의 현재 이용 가능한 수량을 리턴하기 위해 자원이 amountAvailable 메시지로 응답함을 확인할 수 있다. 이 메시지는 inquireFor:ofResource:과 연관된 메서드에서 전송된다.


SimulationObject 메시지 acquire:ofResource:과 acquire:ofResource:withPriority:과 연관된 메서드에서 ResourceProvider가 얻어지고 acquire: amount withPriority: priorityNumber 메시지가 전송된다. 메시지를 전송한 결과는 StaticResource 클래스의 인스턴스가 된다. 하지만 양을 이용할 수 없는 경우 요청이 이루어진 프로세스는 필요한 자원이 이용 가능해질 때까지 보류될 것이다. 획득한 자원을 재활용하기 위해 StaticResource로 release 메시지가 전송된다.


SimulationObject가 조정된 자원을 요청하고 (acquireResource:) 그것이 성공하면 객체는 특정 작업이 (서비스가) 완료될 때까지 자원의 역할을 하는 다른 시뮬레이션 객체를 (서비스가 필요한 객체) 끌어들인다. 그러한 자원을 이용할 수 없는 경우, 요청이 이루어진 프로세스는 필요한 자원이 이용 가능해질 때까지 보류될 것이다. ResourceCoordinator 클래스의 인스턴스는 서비스 작업을 조정하기 위한 요청을 하기 위해 acquire 메시지를 이해하고, 활동의 동기화를 위해 다른 객체가 인자를 끌어들일 것을 명시하기 위해 producedBy: aSimulationObject를 이해한다. SimulationObject의 구현에서 나타난 바와 같이 ResourceCoordinator는 자원(손님) 또는 서비스 제공자(종업원)가 활동을 조정하길 기다리는지 결정하기 위해 customersWaiting 또는 serversWaiting과 같은 쿼리, 그리고 얼마나 많은 수가 기다리고 있는지 알리기 위해 queueLength와 같은 쿼리에 응답할 수 있다.


ResourceProvider와 ResourceCoordinator 클래스의 구현 설명은 24장과 25장에서 제공하겠다.


DelayedEvent 클래스

Simulation 클래스에 대한 스케줄링 메커니즘의 구현은 다중 프로세스를 다룬 장에서 (15장) 제시한 Smalltalk-80 프로세서 스케줄러 클래스를 광범위하게 사용한다. Simulation 클래스의 디자인에는 해결해야 할 문제가 몇 가지 있다. 첫째, 시뮬레이트된 시간의 증가량 동안 지연되어야 할 이벤트를 어떻게 저장하는가? 둘째, 특정 시간에 시작된 모든 프로세스가 시계를 변경하기 전에 완료되어야 한다면 어떻게 해야 하는가? 셋째, 앞의 두 문제에 대한 해답과 관련해 액션의 순서, 특히 특정 유형의 SimulationObject의 시작과 인스턴스화를 반복적으로 스케줄링하기 위한 요청은 어떻게 구현하는가?


첫 번째 문제를 해결하기 위해 Simulation은 모든 계획된 이벤트의 큐를 유지한다. 이러한 큐는 SortedCollection으로서, 호출되어야 하는 시뮬레이트된 시간에 따라 정렬된 이벤트를 요소로 가진다. 큐에 있는 각 이벤트는 DelayEvent 클래스의 인스턴스인 패키지 내에 위치된다. 패키지가 생성될 당시 이벤트는 시스템의 활성 프로세스이다. 따라서 Semaphore를 생성함으로써 필요한 실행 컨텍스트를 이용해 저장할 수 있다. 이벤트가 큐에 추가되면 DelayedEvent에 pause 메시지가 전송되고, 이는 해당하는 Semaphore에게 wait 메시지를 전송하는데, 이벤트가 큐에서 사라지면 resume 메시지를 전송함으로써 계속된다. resume과 연관된 메서드는 DelayedEvent의 Semaphore에게 signal 메시지를 전송한다.


DelayedEvent 클래스의 인스턴스에 대한 프로토콜은 5개의 메시지로 구성된다.

접근하기(accessing)
condition 이벤트가 시퀀스되어야 하는 조건을 응답하라.
condition: anObject 인자 anObject를 이벤트가 시퀀스되어야 하는 상태로 설정한다.
스케줄링(scheduling)
pause 현재 활성 프로세스, 즉 현재 실행 중인 이벤트를 보류한다.
resume 보류된 프로세스를 재개한다.
비교하기(comparing)
< = aDelayedEvent 수신자가 인자 aDelayedEvent 이전에 시퀀스되어야 하는지 응답한다.
DelayedEvent 인스턴스 프로토콜


DelayedEvent는 클래스로 new 또는 onCondition: anObject 메시지를 전송하여 생성된다. DelayedEvent 클래스의 구현은 다음과 같다.

클래스명 DelayedEvent
슈퍼클래스 Object
인스턴스 변수명 resumptionSemaphore resumptionCondition
클래스 메서드
instance creation
    new
        super new initialize
    onCondition: anObject
        super new setCondition: anObject
인스턴스 메서드
accessing
    condition
        resumptionCondition
    condition: anObject
        resumptionCondition  anObject

comparing
< = aDelayedEvent
    resumptionCondition isNil
        ifTrue: [true]
        ifFalse: [resumptionCondition < = aDelayedEvent condition]

scheduling
    pause
        resumptionSemaphore wait
    resume
        resumptionSemaphore signal.
        resumptionCondition

private
    initialize
        resumptionSemaphore  Semaphore new
    setCondition: anObject
        self initialize.
        resumptionCondition  anObject


위의 명세에 따르면 resumption 상태로 사용되는 객체라면 무엇이든 <=에 응답해야 하는데, 일반적으로 SimulationObject가 그러한 상태이다.


Simulation 클래스

Simulation 클래스의 인스턴스는 네 가지 인스턴스 변수를 소유한다. 시뮬레이션의 자원으로서 (resources) 행동하는 객체의 Set, 현재 시간을 나타내는 (currentTime) Number, 지연된 이벤트의 큐를 (eventQueue) 나타내는 SortedCollection, 현재 시간에 활성화된 프로세스의 개수를 표시하는 (processCount) Integer가 그것들이다.


Simulation을 초기화하면 인스턴스 변수를 초기값으로 설정한다. 인스턴스에 스케줄링 메시지 startUp을 전송하면 스스로에게 activate 메시지를 전송하여 관련된 다른 클래스들에게 어떤 Simulation이 현재 활성화되었는지 알려준다.

클래스명 Simulation
슈퍼클래스 Object
인스턴스 변수명 resources currentTime
eventQueue processCount
클래스 메서드
instance creation
    new
        super new initialize
인스턴스 메서드
initialization
    initialize
        resources  Set new.
        currentTime  0.0.
        processCount  0.
        eventQueue  SortedCollection new
    activate
        "This instance is now the active simulation. Inform class SimulationObject."
        SimulationObject activeSimulation: self.
        "Resource is the superclass for ResourceProvider and ResourceCoordinator"
        Resource activeSimulation: self


초기화 메시지는 서브클래스에서도 필요로 한다. 모델러가 도착 스케줄과 자원 객체를 명시하는 데 사용하도록 제공되는 메시지들은 프로세스 스케줄링 메시지에 대한 인터페이스를 제공한다.

initialization
    defineArrivalSchedule
        "A subclass specifies the schedule by which simulation objects dynamically enter into the simulation."
        self
    defineResources
        "A subclass specifies the simulation objects that are initially entered into the simulation."
        self
task language
    produce: amount of: resourceName
        (self includesResourceFor: resourceName)
            ifTrue: [(self provideResourceFor: resourceName) produce: amount]
            ifFalse: [resources add: (ResourceProvider named: resourceName with: amount)]
    coordinate: resourceName
        (self includesResourceFor: resourceName)
            ifFalse: [resources add: (ResourceCoordinator named: resourceName)]
    schedule: actionBlock after: timeDelay
        self schedule: actionBlock at: currentTime + timeDelay
    schedule: aBlock at: timeInteger
        "This is the mechanism for scheduling a single action"
        self newProcessFor:
            [self delayUntil: timeInteger.
                aBlock value]
    scheduleArrivalOf: aSimulationObject at: timeInteger
        self schedule: [aSimulationObject startUp] at: timeInteger
    scheduleArrivalOf: aSimulationObjectClass accordingTo: aProbabilityDistribution
        "This means start now"
        self scheduleArrivalOf: aSimulationObjectClass
            accordingTo: aProbabilityDistribution
            startingAt: currentTime
    scheduleArrivalOf: aSimulationObjectClass accordingTo: aProbabilityDistribution startingAt: timeInteger
        "Note that aClass is the class SimulationObject or one of its subclasses. The real work is done in the private message schedule:startingAt:andThenEvery:"
        self schedule: [aSimulationObjectClass new startUp]
            startingAt: timeInteger
            andThenEvery: aProbabilityDistribution


task language의 스케줄링 메시지는 인스턴스화된 프로세스를 추적하기 위한 참조-계수 해답을 구현한다. 이는 앞서 제시한 두 번째 문제, 즉 다른 프로세스가 시계를 변경할 수 있는 기회를 얻기 전에 하나의 Smalltalk-80 프로세스 스케줄러가 특정 시간에 모든 프로세스를 시작하도록 만드는 방법에 대한 문제를 해결하는 데 사용한 기법이다. 참조 계수를 이용하면 참조 계수가 0이 아닌 한 시뮬레이트된 시간이 변경되지 않도록 보장할 수 있다.


주요 메서드는 schedule: aBlock at: timeInteger와 schedule: aBlock startingAt: timeInteger andThenEvery: aProbabilityDistribution에 연관된 메서드들이다. 두 번째 메시지는 private 메시지로, scheduleArrivalOf: aSimulationObjectClass accordingTo: aProbabilityDistribution startingAt: timeInteger와 연관된 메서드에 의해 호출된다. 이 메시지는 반복되는 액션의 스케줄링을 위한 일반 매커니즘을 제공하고, 그에 따라 앞서 언급한 세 번째 디자인 문제, 즉 액션의 시퀀스를 반복하여 스케줄링하기 위한 요청을 구현하는 방법에 대한 해답을 제시한다.


schedule: aBlock at: timeInteger은 시뮬레이션이 적절한 시뮬레이트된 시간에 (timeInteger) 도달할 때까지 액션의 시퀀스의 (aBlock) 평가를 지연하는 프로세스를 생성한다는 개념을 중심으로 이루어진다. 지연은 delayUntil: delayedTime 메시지로 실행된다. 연관된 메서드는 시뮬레이션의 이벤트 큐로 추가될 DelayedEvent를 생성한다. 이후 이러한 DelayedEvent와 연관된 프로세스가 보류된다 (pause 메시지를 전송하여). DelayedEvent의 인스턴스가 큐에서 첫 번째인 경우 인스턴스가 제거되고 시간은 저장된 (지연된) 시간으로 이동될 것이다. 그리고 해당 DelayedEvent의 인스턴스로 resume 메시지가 전송되어 블록이 평가되고, 이러한 블록은 특정 시뮬레이션 활동을 스케줄링하는 액션을 실행한다.


활성화된 프로세스는 DelayedEvent가 기다리라는 신호를 받으면 보류된다. 따라서 프로세스 개수의 계수는 감소되어야 한다 (stopProcess). DelayedEvent가 재개되면 프로세스는 delayedUntil: 메서드 내 마지막 표현식을 이용해 평가를 계속하고, 프로세스 개수의 계수는 증가될 것이다 (startProcess).

scheduling
    delayUntil: aTime
        | delayEvent |
        delayEvent  DelayedEvent onCondition: timeInteger.
        eventQueue add: delayEvent.
        self stopProcess.
        delayEvent pause.
        self startProcess
    delayFor: timeDelay
        self delayUntil: currentTime + timeDelay
    startProcess
        processCount  processCount + 1
    stopProcess
        processCount  processCount - 1


프로세스의 참조 계수 또한 Simulation 클래스의 스케줄링 메시지 newProcessFor: aBlock과 연관된 메서드에서 처리된다. 이는 아래와 같이 구현된다.

newProcessFor: aBlock
    self startProcess.
    [aBlock value.
        self stopProcess] fork


첫 번째 표현식은 프로세스의 계수를 증가시킨다. 두 번째 표현식은 분기된(forked) 블록이다. Smalltalk 프로세스 스케줄러가 해당 블록을 평가하면 액션의 시뮬레이션 시퀀스인 aBlock이 평가된다. aBlock의 평가가 완료되면 프로세스 계수를 감소시켜야 한다는 신호를 보낸다. 이렇게 액션의 단일 시퀀스가 Simulation의 이벤트 큐에서 스케줄링되고 올바른 시뮬레이트 시간에 도달할 때까지 지연된다. 요약하자면, 프로세스의 참조 계수는 새로운 액션의 시퀀스가 시작될 때마다 증가하고, 시퀀스가 완료될 때마다 감소하며, DelayedEvent가 생성될 때마다 감소하고 DelayedEvent가 계속될 때마다 증가한다.


Private 메시지인 schedule: aBlock startingAt: timeInteger andThenEvery: aProbabilityDistribution에 대한 메서드는 반복적으로 액션을 스케줄링하는 프로세스를 분기한다. 알고리즘은 두 가지 메시지의 반복으로 구성된다.

self delayUntil: timeInteger.
self newProcessFor: aBlock


두 개의 메시지, delayUntil: 과 newProcessFor: 의 반복은 확률 분포 함수에 따라 좌우된다. 반복 횟수는 분포를 표본화할 수 있는 횟수와 동일하다. 표본화되는 횟수는 다음으로 액션의 시퀀스를 (aBlock) 호출해야 하는 시간을 나타낸다. 분포의 요소는 do: 메시지를 분포로 전송하여 열거된다.

private
    schedule: aBlock
            startingAt: timeInteger
            andThenEvery: aProbabilityDistribution
        self newProcessFor:
            ["This is the first time to do the action."
                self delayUntil: timeInteger.
                "Do the action."
                self newProcessFor: aBlock copy.
                aProbabilityDistribution
                    do: [ :nextTimeDelay |
                        "For each sample from the distribution, delay the amount sampled."
                        self delayFor: nextTimeDelay.
                        "then do the action"
                        self newProcessFor: aBlock copy]]


Simulation 자체는 SimulationObject의 것과 유사한 제어 프레임워크를 갖고 있다. startUp에 대한 응답으로 시뮬레이션을 현재 활성화된 시뮬레이션으로 만들고, 시뮬레이션 객체와 도착 스케줄을 정의한다. 스케줄링된 활동의 내부 루프는 proceed 메시지에 대한 응답으로 제공된다. Simulation이 proceed 메시지를 수신할 때마다 프로세스의 참조 계수를 확인한다 (readyToContinue 메시지를 전송함으로써). 참조 계수가 0이 아니라면 현재 시뮬레이트된 시간에 활성 프로세스가 여전히 존재한다는 뜻이다. 따라서 시스템 규모의 프로세서인 Processor에게 제어를 넘겨주어 이러한 프로세스가 진행되도록 놔둘 것을 요청한다. 참조 계수가 0일 경우, 이벤트 큐를 검사한다. 이벤트 큐가 비어 있지 않은 경우, 다음 이벤트가 이벤트 큐로부터 제거되고 시간이 변경되며 지연된 프로세스가 재개된다.

simulation control
    startUp
        self activate.
        self defineResources.
        self defineArrivalSchedule
    proceed
        | eventProcess |
        [self readyToContinue] whileFalse: [Processor yield].
        eventQueue isEmpty
            ifTrue: [self finishUp]
            ifFalse: [eventProcess  eventQueue removeFirst.
                currentTime  eventProcess time.
                eventProcess resume]
    finishUp
        "We need to empty out the event queue"
        eventQueue  SortedCollection new.
        false
    enter: anObject
        self
    exit: anObject
        self
private
    readyToContinue
        processCount = 0


이러한 다양한 모델러의 언어와 시뮬레이션 제어 메시지 외에도 Simulation의 프로토콜에는 여러 접근 메시지가 제공된다.

accessing
    includesResourceFor: resourceName
        | test |
        test  resources
            detect: [ :each | each name = resourceName]
            ifNone: [nil].
        test notNil
    provideResourceFor: resourceName
        resources detect: [ :each | each name = resourceName]
    time
        currentTime


Simulation과 SimulationObject의 구현은 BlockContext에 fork 메시지를, ProcessorScheduler에 yield 메시지를, Semaphore에 signal과 wait 메시지를 사용하는 모습을 설명한다.


NothingAtAll 예제 추적하기

이제 시뮬레이션의 첫 번째 예제, DoNothings만 스케줄링되었던 NothingAtAll의 실행을 추적할 수 있겠다. 아래의 메시지를 전송하고 나면

NothingAtAll new


새로운 시뮬레이션의 인스턴스 변수는 아래와 같이 구성된다.

resources = Set ()
currentTime = 0.0
processCount = 0
eventQueue = SortedCollection ()


이 시뮬레이션으로 startUp 메시지를 전송하면 scheduleArrivalOf: DoNothing accordingTo: (Uniform from: 1 to: 5)을 호출한다. 이는 시뮬레이션으로 다음의 메시지를 전송하는 것과 동일하다.

schedule: [DoNothing new startUp]
startingAt: 0.0
andThenEvery: (Uniform from: 1 to: 5)


이에 대한 응답으로 newProcessfor: 에서 호출된다.


단계 1. newProcessFor: 는 processCount(self startProcess)를 증가시킨 다음 블록 A라고 부르게 될 아래의 블록을 평가하는 새로운 Process를 생성한다.

[self delayUntil: timeInteger.
    self newProcessFor: block copy.
    aProbabilityDistribution do:
        [ :nextTimeDelay |
            self delayFor: nextTimeDelay.
            self newProcessFor: block copy]


여기서 변수 block은 다음과 같으며,

[DoNothing new startUp]


블록 B로 부를 것이다.


단계 2. 프로세스가 newProcessFor: 메서드의 두 번째 표현식에 리턴하면 블록 A를 평가하여 실행이 계속된다. 그것의 첫 번째 표현식은 프로세스 계수를 감소시키고 시간이 0.0이 될 때까지 활동을 보류한다 (예: 활성 시뮬레이션 스케줄러를 대상으로 DelayedEvent를 생성하여 0.0 시간에 startUp 하도록 DoNothing에게 알려주도록 하고, 그것을 이벤트 큐에 추가한다).

resources = Set ()
currentTime = 0.0
processCount = 0
eventQueue = SortedCollection (a DelayedEvent 0.0)


이제 메시지 proceed를 시뮬레이션으로 전송한다. 프로세스 계수는 0이므로 readyToContinue는 true를 리턴하고, 이벤트 큐는 비어있지 않으므로 첫 번째 DelayedEvent가 제거되어 시간이 0.0으로 설정되며, 지연된 프로세스가 재개된다 (이것은 스케줄러 블록 A였다). 다음 액션은 프로세스 계수를 증가시키고 블록 B를 평가한다. 이는 DoNothing이 들어와 그 작업을 수행하도록 하는데, 여기서 작업이라 함은 아무 일도 하지 않고 곧바로 나가는 것을 의미한다. 새로운 processFor: 메시지는 프로세스 계수를 0으로 감소시키고, 확률 분포로부터 숫자를 얻어 해당하는 시간 단위 동안 지연시키며, 어느 정도 시간이 흐른 후 새로운 프로세스를 스케줄링한다. 시퀀스는 proceed 메시지가 시뮬레이션으로 재전송되는 한 무기한으로 계속된다.


다른 작업들은 SimulationObject의 서브클래스에서 tasks 메시지와 연관된 메서드에 따라 이벤트 큐로 들어갈 것이다. 따라서 Visitor가 NothingAtAll에서 스케줄링될 경우 self holdFor: someTime 표현식이 큐에 있는 이벤트로 들어가고, 새로 도착하는 Visitors와 DoNothings를 스케줄링하는 이벤트들과 섞이게 된다.


Notes