Smalltalk80LanguageImplementationKor:Chapter 24

From 흡혈양파의 번역工房
Jump to navigation Jump to search
제 24 장 이벤트 주도 시뮬레이션에서 자원의 사용

이벤트 주도 시뮬레이션에서 자원의 사용

앞의 여러 장에 걸쳐 이벤트 주도 시뮬레이션을 명시하고 그러한 시뮬레이션에 대한 통계를 수집하는 프레임워크를 소개하였다. 확보하거나 사용할 자원이 없을 경우 객체가 시뮬레이션에서 실행할 수 있는 유일한 작업은 명시된 시뮬레이트 시간만큼 참는 (기다리는) 것이다. 이번 장에서는 두 가지 유형의 자원을 소개하고자 하는데, 고정 자원과 변동 자원이다. 이러한 자원들은 22장에서 Simulation과 SimulationObject 클래스를 예로 들어 소개하면서 자원 조정의 지원을 정의하였는데 이를 상세히 살펴볼 예정이다.


고정 자원은 "consumable"(소모 가능)하다. 소모 가능 자원을 이용한 시뮬레이션은 어떤 자원, 가령 젤리 빈의 주어진 수량으로 시작된다. 시뮬레이션이 진행되면서 객체는 자원을 얻고, 결국 본래 이용할 수 있었던 자원을 모두 사용하게 된다. 자원을 필요로 하는 새로운 객체는 작업을 성공적으로 완료하지 못하고 종료되거나 절대로 제공되지 않을 자원을 무기한으로 기다리느라 지연된다. “Nonconsumable”(소모 불가)한 고정 자원도 있다. 시뮬레이션은 소모 불가 자원, 즉 유리병과 같은 자원의 주어진 수량으로 시작된다. 객체가 더 이상 자원을 필요로 하지 않으면 재활용된다. 자원을 필요로 하는 객체는 자원을 즉시 얻거나 재활용된 자원이 이용 가능해질 때까지 지연된다.


변동 자원은 생산자/소비자 관계를 모델화한다. 시뮬레이션은 어떤 자원, 예를 들어 자동차의 주어진 수량으로 시작할 수 있다. 시뮬레이션이 진행되면서 객체는 자원을 획득하고, 자원이 더 이상 필요하지 않으면 재활용한다 (예: 중고 자동차 시장). 새로운 자원이 추가되고 (생산), 자원의 공급이 증가한다. 자원을 필요로 하는 객체는 즉시 얻거나 재활용된 자원 또는 새로운 자원이 이용 가능해질 때까지 기다려야 한다. 추가 수량을 생산할 수 있는 변동 자원을 “renewable”(재생 가능) 자원이라 부른다. 자동차 예제는 재생 가능하면서 소모 불가 자원의 예제에 속한다.


ResourceProvider와 StaticResource 구현하기

Simulation에서 자원을 명시하기 위한 모델러의 언어는 아래 형태의 표현식을 포함한다.

self produce: amount of: resourceName


이러한 메시지에 대한 응답으로는, 이용 가능한 자원의 수량이 amount인 Resource 클래스의 인스턴스를 생성하거나, 기존의 Resource를 검색하여 자원을 amount만큼 증가시킨다. ResourceProvider의 인스턴스는 ResourceProvider로 named: aString 또는 named: aString with: amount 메시지를 전송하여 생성된다.


SimulationObject가 자원을 요청하면 (acquire: amount ofResource: resourceName), 현재 활성화된 시뮬레이션(ActiveSimulation)에게 해당하는 자원을 제공할 것을 (provideResourceFor:) 요청한다. 아마 자원은 Simulation을 초기화한 결과로 시뮬레이션에 존재할 것이며, Simulation은 ResourceProvider의 인스턴스로 된 Set을 참조하고 이러한 Set을 열거하여 resourceName이란 이름을 가진 대상을 찾을 것이다.


SimulationObject가 ResourceProvider로 접근하고 나면,

얼마나 많은 자원을 이용할 수 있는지 질문할 수 있고,
그 이름을 질문할 수 있고,
일부 양만큼 (그리고 특정한 접근 우선순위로) 획득하도록 요청할 수 있고,
일부 양만큼 생산하도록 요청할 수 있다.


SimulationObject가 일부 자원을 획득하도록 요청하면 이러한 요청은 요청의 큐로 추가되어 우선순위대로 정렬되고, 우선순위의 수준이 동일한 요청 중에서는 먼저 들어온 순대로 처리된다. 요청이 이루어지거나 더 많은 자원이 생성될 때마다 ResourceProvider는 보류 요청 중 하나 또는 그 이상을 충족시킬 수 있는지 확인한다.


각 요청은 DelayedEvent 클래스의 인스턴스로 저장된다. DelayedEvent는 22장에서 설명한 바 있다. 각 DelayedEvent는 상태를 참조한다. 지연된 작업의 경우, 상태는 작업을 재개해야 하는 시간을 나타내고, 자원 요청의 경우 상태는 요청된 자원과 바람직한 양을 표현하는 StaticResource의 인스턴스가 된다. 요청이 이루어질 때마다 요청은 대기 큐 pending에 저장된 후 자원을 제공하기 위한 시도가 이루어진다.


ResourceProvider 클래스는 Resource 클래스의 서브클래스이다. 다음 장에서 소개할 ResourceCoordinator 클래스 또한 Resource의 서브클래스이다. Resource는 자원의 이름과 충족되어야 하는 요청의 큐와 관련해 자원을 표현한다. 클래스 변수는 시간과 프로세스 참조 계수로 접근을 위해 현재 활성화된 시뮬레이션을 (ActiveSimulation) 참조한다.

클래스명 Resource
슈퍼클래스 Object
인스턴스 변수명 pending
resourceName
클래스 변수명 ActiveSimulation
클래스 메서드
class initialization
    activeSimulation: existingSimulation
        ActiveSimulation  existingSimulation
instance creation
    named: resourceName
        self new setName: resourceName
인스턴스 메서드
accessing
    addRequest: aDelayedEvent
        pending add: aDelayedEvent.
        self provideResources.
        ActiveSimulation stopProcess.
        aDelayedEvent pause.
        ActiveSimulation startProcess
    name
        resourceName

private
    provideResources
        self
    setName: aString
        resourceName  aString.
        pending  SortedCollection new


요청을 pending이라는 SortedCollection에 저장하는 데 사용된 매커니즘은 지연된 이벤트를 Simulation의 eventQueue에 저장할 때 사용된 것과 비슷하다. 즉, 실행 중이던 프로세스가 보류되어 Simulation에서 프로세스에 대한 참조 계수가 감소한다. 프로세스가 다시 계속되는 시점에서 참조 계수가 증가된다. 시뮬레이션 프로세스의 일시 중지와 재개를 동기화하기 위해 Semaphore가 사용된다.


ResourceProvider 클래스는 해야 할 작업이 없는 간단한 quantifiable 항목으로 자원을 표현하고, 그에 따라 사실상 SimulationObjects로서 생성되지 않는다. 오히려 항목의 개수가 수치형 계수(numerical count)에 유지된다. SimulationObject가 이러한 유형의 자원을 성공적으로 획득하면 SimulationObject는 StaticResource의 인스턴스로 접근성을 부여받는다. ResourceProvider 내에서 acquire:withPriority:과 연관된 마지막 표현식은 StaticResource를 생성하여 리턴한다. 이 표현식을 평가하기 전에 보류 요청의 컬렉션으로부터 DelayedEvent가 제거되고, 현재 이용 가능한 양에서 요청된 양만큼 감소된다.

클래스명 ResourceProvider
슈퍼클래스 Resource
인스턴스 변수명 amountAvailable
클래스 메서드
instance creation
    named: resourceName with: amount
        self new setName: resourceName with: amount
    named: resourceName
        self new setName: resourceName with: 0
인스턴스 메서드
accessing
    amountAvailable
        amountAvailable

task language
    acquire: amountNeeded withPriority: priorityNumber
        | anEvent |
        anEvent  DelayedEvent onCondition: (StaticResource for: amountNeeded of: self withPriority: priorityNumber).
        self addRequest: anEvent.
        anEvent condition
    produce: amount
        amountAvailable  amountAvailable + amount.
        self provideResources

private
    provideResources
        | anEvent |
        [pending isEmpty not
            and: [pending first condition amount < = amountAvailable]]
            whileTrue:
                [anEvent  pending removeFirst.
                    amountAvailable  amountAvailable - anEvent condition amount.
                    anEvent resume]
    setName: resourceName with: amount
        super setName: aString.
        amountAvailable  amount


StaticResource는 일부 다른 SimulationObject에 대한 항목의 수량을 보관하는 것 외에는 다른 작업이 없는 SimulationObject를 나타낸다.

클래스명 StaticResource
슈퍼클래스 SimulationObject
인스턴스 변수명 amount
resource
priority
클래스 메서드
instance creation
    for: amount of: aResource withPriority: aNumber
        self new setAmount: amount resource: aResource withPriority: aNumber
인스턴스 메서드
accessing
    amount
        amount
    name
        resource name
    priority
        priority

comparing
< = aStaticResource
    priority < = aStaticResource priority

task language
    consume: aNumber
        amount  (amount - aNumber) max: 0
    release
        resource produce: amount.
        amount  0

private
    setAmount: aNumber resource: aResource withPriority: priorityNumber
        amount  aNumber.
        resource  aResource.
        priority  priorityNumber


SimulationObject는 StaticResource에 대한 참조를 보유할 수 있으므로 consume: 과 release 메시지는 SimulationObject의 작업 언어에 일부가 된다. SimulationObject가 자원을 얻으면 아마 자원이 시뮬레이션에 반환될 때까지 해당 자원을 소모할 것이다. StaticResource에 보유된 자원의 양은 StaticResource로 release 메시지를 전송하여 시뮬레이션에 반환된다. (하지만 일반적으로는 SimulationObject가 스스로에게 release: aStaticResource 메시지를 전송하여 객체의 작업과 연관된 메서드에서 self로 전송되는 메시지와 균일한 스타일이 유지되도록 한다.) 이러한 동일화는 시뮬레이션이 이벤트를 추적하고 감시하는 메서드의 디자인을 간소화한다. 모든 작업 메시지는 self에 대해, 그에 따라 SimulationObject에 대한 메시지로서 전송되기 때문에 작업이 이루어지고 있다는 표기법을 저장하기 위해 모든 작업 메시지를 가로채는(intercepted) SimulationObject의 서브클래스를 생성하는 것이 가능하다 (EventMonitor는 23장에서 소개한 예제이다).


acquire:ofResource: 과 release:을 사용하면 자원이 소모 불가 자원으로 취급된다. 두 가지를 섞는 것도 가능하다. SimulationObject는 특정 양만큼 자원을 얻는 것이 가능한데, 가령 10개 자원 중에서 5개를 소모하고 5개를 리턴할 수 있다. consume: 메시지는 시뮬레이션으로부터 자원을 제거 시 사용된다. 따라서 10개 자원으로 얻은 StaticResource에 consume: 5 메시지를 전송하고 release 메시지를 전송하면 (또는 release: aStaticResource to self) 된다.


소모 가능 자원(Consumable Resources)

간단한 젤리 빈 예제로 소모 가능 자원의 개념이 설명된다. 22장에서 소개한 시뮬레이션 예제 NothingAtAll을 생각해보자. Visitor가 시뮬레이션으로 들어와 둘러볼 때마다 2개의 젤리 빈을 얻고 빈을 먹는 데 시간 단위 2를 소요한 후 떠나는 작업을 갖고 있다. 시뮬레이션은 15개의 젤리 빈으로 구성된 하나의 자원으로 초기화된다. 이러한 NothingAtAll 버전의 정의는 다음과 같다.

클래스명 NothingAtAll
슈퍼클래스 Simulation
인스턴스 메서드
initialization
    defineResources
        self produce: 15 of: 'jelly beans'
    defineArrivalSchedule
        self scheduleArrivalOf: Visitor accordingTo: (Uniform from: 1 to: 3)


Visitor의 작업은 tasks에 대한 메서드에서 발견된 self로 전송되는 두 개의 메시지로 표현된다.

tasks
    self acquire: 2 ofResource: 'jelly beans'.
    self holdFor: 2


나가는 것과 들어오는 것을 감시하는 이러한 시뮬레이션의 실행 예제는 다음과 같다.

0.0 Visitor 1 enters
2.0 Visitor 1 exits
2.03671 Visitor 2 enters
4.03671 Visitor 2 exits
4.34579 Visitor 3 enters
5.92712 Visitor 4 enters
6.34579 Visitor 3 exits
7.92712 Visitor 4 exits
8.46271 Visitor 5 enters
10.4627 Visitor 5 exits
10.5804 Visitor 6 enters
12.5804 Visitor 6 exits
12.7189 Visitor 7 enters
14.7189 Visitor 7 exits 젤리 빈을 얻고자 하는 마지막 방문자는 이제부터 무기한으로 기다린다
15.0638 Visitor 8 enters
17.6466 Visitor 9 enters
19.8276 Visitor 10 enters


7번째 Visitor가 들어서고 나면 더 이상 젤리 빈이 남아있지 않기 때문에 잇따른 Visitors는 모두 절대로 이용 할 수 없는 자원을 무한대로 기다리며 지연된다. 아니면 Visitor가 이용 가능한 젤리 빈이 남아 있는지 확인할 수도 있다 (inquireFor:ofResource:). 하나도 남아 있지 않다면 Visitor는 큐에 갇히느니 떠나는 방법을 택할 수도 있다. 이는 tasks에 대한 아래의 메서드에 해당한다.

tasks
    (self inquireFor: 2 ofResource: 'jelly beans')
        ifTrue: [self acquire: 2 ofResource: 'jelly beans'
                    self holdFor: 2]


한 가지 추가 정제(refinement)로, 시뮬레이션에게 모든 자원이 사용되었고 "가게를 닫아야 할" 시간임을 알리는 것이 되겠다. 이는 Visitor에게 stopSimulation 메시지를 전송하면 된다. 젤리 빈을 얻을 수 없는 Visitor가 처음으로 들어올 때 이 메시지를 전송할 경우 가게에 들어온 Visitor가 갇히게 될 가능성이 있다. 마지막 젤리 벤을 얻은 Visitor가 나가면서 가게 문이 닫히도록 확보해야만 이후 들어온 마지막 Visitor가 가게에서 갇히지 않는다. 이는 tasks에 대한 아래의 메서드에 상응한다.

tasks
    | flag |
    (self inquireFor: 2 ofResource: 'jelly beans')
        ifTrue: [self acquire: 2 ofResource: 'jelly beans'.
            flag  self inquireFor: 2 ofResource: 'jelly beans'.
            "Are there still 2 left so that the next Visitor can be served?"
            self holdFor: 2.
            flag ifFalse: [self stopSimulation]]


나가는 것과 들어오는 것이 감시되는 NothingAtAll 시뮬레이션의 또 다른 실행 예를 소개하겠다.

0.0 Visitor 1 enters
2.0 Visitor 1 exits
2.26004 Visitor 2 enters
4.26004 Visitor 2 exits
4.83762 Visitor 3 enters
6.34491 Visitor 4 enters
6.83762 Visitor 3 exits
8.34491 Visitor 4 exits
8.51764 Visitor 5 enters
9.9006 Visitor 6 enters
10.5176 Visitor 5 exits
11.9006 Visitor 6 exits
12.6973 Visitor 7 enters last successful requestor
14.0023 Visitor 8 enters nothing available for this Visitor
14.0023 Visitor 8 exits
14.6973 Visitor 7 exits last successful requestor closes shop on exit


앞 장에서 설명한 EventMonitor 클래스도 자원의 사용을 감시한다. 따라서 NothingAtAll의 trace는 젤리 빈을 요청하고 획득한 횟수를 포함한다.

0.0 Visitor 1 enters
0.0 Visitor 1 requests 2 of jelly beans
0.0 Visitor 1 obtained 2 of jelly beans
0.0 Visitor 1 holds for 2
1.40527 Visitor 2 enters
1.40527 Visitor 2 requests 2 of jelly beans
1.40527 Visitor 2 obtained 2 of jelly beans
1.40527 Visitor 2 holds for 2
2.0 Visitor 1 exits
2.56522 Visitor 3 enters
2.56522 Visitor 3 requests 2 of jelly beans
2.56522 Visitor 3 obtained 2 of jelly beans
2.56522 Visitor 3 holds for 2
3.40527 Visitor 2 exits
4.56522 Visitor 3 exits
5.3884 Visitor 4 enters
5.3884 Visitor 4 requests 2 of jelly beans
5.3884 Visitor 4 obtained 2 of jelly beans
5.3884 Visitor 4 holds for 2
6.69794 Visitor 5 enters
6.69794 Visitor 5 requests 2 of jelly beans
6.69794 Visitor 5 obtained 2 of jelly beans
6.69794 Visitor 5 holds for 2
7.3884 Visitor 4 exits
7.72174 Visitor 6 enters
7.72174 Visitor 6 requests 2 of jelly beans
7.72174 Visitor 6 obtained 2 of jelly beans
7.72174 Visitor 6 holds for 2
8.69794 Visitor 5 exits
9.72174 Visitor 6 exits
10.153 Visitor 7 enters
10.153 Visitor 7 requests 2 of jelly beans
10.153 Visitor 7 obtained 2 of jelly beans
10.153 Visitor 7 holds for 2
11.875 Visitor 8 enters
11.875 Visitor 8 exits
12.153


11.875 시간에 하나의 젤리 빈만 소모되었다. 12.153 시간에는 Visitor 7번이 시뮬레이션을 중단한다.


소모 불가 자원(Nonconsumable Resources)

자동차 대여 시뮬레이션은 소모 불가 자원의 사용을 설명한다. 15대의 승용차와 3대의 트럭으로 개업한 단기 자동차 대여점을 시뮬레이션 예제로 들겠다. 승용차의 경우 매 30분마다 평균 한 명의 대여자가 도착하고, 트럭을 필요로 하는 대여자는 120분마다 한 명씩 도착한다. 첫 번째 승용차 대여자는 가게가 문을 열 때 도착하고 첫 번째 트럭 대여자는 그로부터 10분 후에 도착한다. CarRenter와 TruckRenter 클래스는 이러한 시뮬레이션 객체를 표현한다.


RentalAgency는 Simulation의 서브클래스로서 명시된다. 이는 두 개의 초기화 메시지, defineArrivalSchedule과 defineResources를 구현한다.

클래스명 RentalAgency
슈퍼클래스 Simulation
인스턴스 메서드
initialization
    defineArrivalSchedule
        self scheduleArrivalOf: CarRenter
            accordingTo: (Exponential mean: 30).
        self scheduleArrivalOf: TruckRenter
            accordingTo: (Exponential mean: 120)
            startingAt: 10
    defineResources
        self produce: 15 of: 'car'
        self produce: 3 of: 'truck'


CarRenter와 TruckRenter의 작업은 비슷하다. 먼저 승용차 또는 트럭을 얻고, 이용할 수 있는 차량이 없다면 기다린다. 자동차를 얻고 나면 사용한다. CarRenter는 4-8시간 동안 자동차를 보관하고 (균일 분포), TruckRenter는 1-4시간 동안 트럭을 보관한다 (균일 분포). 사용 시간은 자동차를 반환하기 전에 (예: 자원 방출) 적절한 시간 동안 대여자가 자동차를 보유하고 있도록 하여 표시된다.


두 가지 종류의 SimulationObject를 관찰하고 각 유형마다 따로 라벨을 붙이기 위해 CarRenter와 TruckRenter의 구현은 앞서 Visitor 클래스에서 보였던 라벨링 기법을 중복한다.

클래스명 CarRenter
슈퍼클래스 EventMonitor
클래스 변수명 CarCounter
Hour
클래스 메서드
class initialization
    file: file
        super file: file
        CarCounter  0.
        Hour  60
인스턴스 메서드
accessing
    setLabel
        CarCounter  CarCounter + 1.
        label  CarCounter printString

simulation control
    tasks
        | car |
        car  self acquire: 1 ofResource: 'car'.
        self holdFor: (Uniform from: 4*Hour to: 8*Hour) next.
        self release: car
클래스명 TruckRenter
슈퍼클래스 EventMonitor
클래스변수명 TruckCounter
Hour
클래스 메서드
class initialization
    file: file
        super file: file.
        TruckCounter  0
        Hour  60
accessing
    setLabel
        TruckCounter  TruckCounter + 1.
        label  TruckCounter printString
인스턴스 메서드
simulation control
    tasks
        | truck |
        truck  self acquire: 1 ofResource: 'truck'.
        self holdFor: (Uniform from: Hour to: 4*Hour) next.
        self release: truck


대여점 시뮬레이션은 아래를 호출하여 실행한다.

aFile  Disk file: 'rental.events'.
CarRenter file: aFile.
TruckRenter file: aFile.
anAgency  RentalAgency new startUp.
[anAgency time < 600] whileTrue: [anAgency proceed]


10시간 이후 (600분) rental.events 파일에 trace는 아래의 이벤트 시퀀스를 표시한다.

0 CarRenter 1 enters
0 CarRenter 1 requests 1 of Car
0 CarRenter 1 obtained 1 of Car
0 CarRenter 1 holds for 460.426
10 TruckRenter 1 enters
10 TruckRenter 1 requests 1 of Truck
10 TruckRenter 1 obtained 1 of Truck
10 TruckRenter 1 holds for 87.2159
26.2079 CarRenter 2 enters
26.2079 CarRenter 2 requests 1 of Car
26.2079 CarRenter 2 obtained 1 of Car
26.2079 CarRenter 2 holds for 318.966
27.4147 CarRenter 3 enters
27.4147 CarRenter 3 requests 1 of Car
27.4147 CarRenter 3 obtained 1 of Car
27.4147 CarRenter 3 holds for 244.867
51.5614 CarRenter 4 enters
51.5614 CarRenter 4 requests 1 of Car
51.5614 CarRenter 4 obtained 1 of Car
51.5614 CarRenter 4 holds for 276.647
78.0957 CarRenter 5 enters
78.0957 CarRenter 5 requests 1 Of Car
78.0957 CarRenter 5 obtained 1 of Car
78.0957 CarRenter 5 holds for 333.212
93.121 CarRenter 6 enters
93.121 CarRenter 6 requests 1 of Car
93.121 CarRenter 6 obtained 1 of Car
93.121 CarRenter 6 holds for 359.718
97.2159 TruckRenter 1 releases 1 of Truck
97.2159 TruckRenter 1 exits
99.0265 CarRenter 7 enters
99.0265 CarRenter 7 requests 1 of Car
99.0265 CarRenter 7 obtained 1 of Car
99.0265 CarRenter 7 holds for 417.572
106.649 CarRenter 8 enters
106.649 CarRenter 8 requests 1 of Car
106.649 CarRenter 8 obtained 1 of Car
106.649 CarRenter 8 holds for 294.43
107.175 CarRenter 9 enters
107.175 CarRenter 9 requests 1 of Car
107.175 CarRenter 9 obtained 1 of Car
107.175 CarRenter 9 holds for 314.198
121.138 CarRenter 10 enters
121.138 CarRenter 10 requests 1 of Car
121.138 CarRenter 10 obtained 1 of Car
121.138 CarRenter 10 holds for 467.032
127.018 TruckRenter 2 enters
127.018 TruckRenter 2 requests 1 of Truck
127.018 TruckRenter 2 obtained 1 of Truck
127.018 TruckRenter 2 holds for 74.5047
145.513 CarRenter 11 enters
145.513 CarRenter 11 requests 1 of Car
145.513 CarRenter 11 obtained 1 of Car
145.513 CarRenter 11 holds for 243.776
166.214 CarRenter 12 enters
166.214 CarRenter 12 requests 1 of Car
166.214 CarRenter 12 obtained 1 of Car
166.214 CarRenter 12 holds for 429.247
172.253 CarRenter 13 enters
172.253 CarRenter 13 requests 1 of Car
172.253 CarRenter 13 obtained 1 of Car
172.253 CarRenter 13 holds for 370.909
191.438 TruckRenter 3 enters
191.438 TruckRenter 3 requests 1 of Truck
191.438 TruckRenter 3 obtained 1 of Truck
191.438 TruckRenter 3 holds for 225.127
201.523 TruckRenter 2 releases 1 of Truck
201.523 TruckRenter 2 exits
220.102 CarRenter 14 enters
220.102 CarRenter 14 requests 1 of Car
220.102 CarRenter 14 obtained 1 of Car
220.102 CarRenter 14 holds for 334.684
252.055 CarRenter 15 enters
252.055 CarRenter 15 requests 1 of Car
252.055 CarRenter 15 obtained 1 of Car
252.055 CarRenter 15 holds for 408.358
269.964 CarRenter 16 enters
269.964 CarRenter 16 requests 1 of Car
272.282 CarRenter 3 releases 1 of Car
272.282 CarRenter 3 exits
272.282 CarRenter 16 obtained 1 of Car
272.282 CarRenter 16 holds for 281.349
292.375 CarRenter 17 enters
292.375 CarRenter 17 requests 1 of Car
328.208 CarRenter 4 releases 1 of Car
328.208 CarRenter 4 exits
328.208 CarRenter 17 obtained 1 of Car
328.208 CarRenter 17 holds for 270.062
345.174 CarRenter 2 releases 1 of Car
345.174 CarRenter 2 exits
350.53 CarRenter 18 enters
350.53 CarRenter 18 requests 1 of Car
350.53 CarRenter 18 obtained 1 of Car
350.53 CarRenter 18 holds for 297.154
354.126 CarRenter 19 enters
354.126 CarRenter 19 requests 1 of Car
358.269 TruckRenter 4 enters
358.269 TruckRenter 4 requests 1 of Truck
358.269 TruckRenter 4 obtained 1 of Truck
358.269 TruckRenter 4 holds for 173.648
367.88 TruckRenter 5 enters
367.88 TruckRenter 5 requests 1 of Truck
367.88 TruckRenter 5 obtained 1 of Truck
367.88 TruckRenter 5 holds for 175.972
389.289 CarRenter 11 releases 1 of Car
389.289 CarRenter 11 exits
389.289 CarRenter 19 obtained 1 of Car
389.289 CarRenter 19 holds for 379.017
401.079 CarRenter 8 releases 1 of Car
401.079 CarRenter 8 exits
402.224 CarRenter 20 enters
402.224 CarRenter 20 requests 1 of Car
402.224 CarRenter 20 obtained 1 of Car
402.224 CarRenter 20 holds for 341.188
410.431 TruckRenter 6 enters
410.431 TruckRenter 6 requests 1 of Truck
411.307 CarRenter 5 releases 1 of Car
411.307 CarRenter 5 exits
416.566 TruckRenter 3 releases 1 of Truck
416.566 TruckRenter 3 exits
416.566 TruckRenter 6 obtained 1 of Truck
416.566 TruckRenter 6 holds for 119.076
421.373 CarRenter 9 releases 1 of Car
421.373 CarRenter 9 exits
422.802 CarRenter 21 enters
422.802 CarRenter 21 requests 1 of Car
422.802 CarRenter 21 obtained 1 of Car
422.802 CarRenter 21 holds for 241.915
452.839 CarRenter 6 releases 1 of Car
452.839 CarRenter 6 exits
460.426 CarRenter 1 releases 1 of Car
460.426 CarRenter 1 exits
512.263 CarRenter 22 enters
512.263 CarRenter 22 requests 1 of Car
512.263 CarRenter 22 obtained 1 of Car
512.263 CarRenter 22 holds for 277.035
516.598 CarRenter 7 releases 1 of Car
516.598 CarRenter 7 exits
531.917 TruckRenter 4 releases 1 of Truck
531.917 TruckRenter 4 exits
535.642 TruckRenter 6 releases 1 of Truck
535.642 TruckRenter 6 exits
543.162 CarRenter 13 releases 1 of Car
543.162 CarRenter 13 exits
543.852 TruckRenter 5 releases 1 of Truck
543.852 TruckRenter 5 exits
553.631 CarRenter 16 releases 1 of Car
553.631 CarRenter 16 exits
554.786 CarRenter 14 releases 1 of Car
554.786 CarRenter 14 exits
574.617 CarRenter 23 enters
574.617 CarRenter 23 requests 1 of Car
574.617 CarRenter 23 obtained 1 of Car
574.617 CarRenter 23 holds for 436.783
588.171 CarRenter 10 releases 1 of Car
588.171 CarRenter 10 exits
591.51 CarRenter 24 enters
591.51 CarRenter 24 requests 1 of Car
591.51 CarRenter 24 obtained 1 of Car
591.51 CarRenter 24 holds for 430.067
595.461 CarRenter 12 releases 1 of Car
595.461 CarRenter 12 exits
598.27 CarRenter 17 releases 1 of Car
598.27 CarRenter 17 exits
599.876 CarRenter 25 enters
599.876 CarRenter 25 requests 1 of Car
599.876 CarRenter 25 obtained 1 of Car
599.876 CarRenter 25 holds for 472.042
642.188 TruckRenter 7 enters
642.188 TruckRenter 7 requests 1 of Truck
642.188 TruckRenter 7 obtained 1 of Truck
642.188 TruckRenter 7 holds for 190.586


시뮬레이션의 스냅숏을 보면 642.188 시간에 아래의 이벤트가 anAgency에 큐로 추가되고 아래의 자원이 이용 가능해진다.

Resource (car) no pending requests; 6 available
Resource (truck) no pending requests; 2 available
Event Queue
    CarRenter for creation and start up
    TruckRenter for creation and start up
    9 CarRenters holding
    1 TruckRenter holding


소모 불가 자원은 SimulationObject가 획득한 자원을 재활용하기 위해 release: 메시지를 전송한다는 점에서만 소모 자원의 설명에 차이가 있다.


파일 시스템 예제(Example of a File System)

자동차 대여는 객체가 (승용차 대여자와 트럭 대여자) 도착하고, 작업을 수행한 후 다시 떠나는 개방형(open) 시뮬레이션이다. 폐쇄형 시뮬레이션은 시뮬레이션 실행 기간 동안 시뮬레이션에 동일한 객체가 남아 있는 시뮬레이션이다. 다음으로는 파일 시스템의 예를 소개할 것인데, 이는 시뮬레이션 기반 시스템 Demos를 소개한 Graham Birtwistle의 저서 [A System for Discrte Event Modelling on Simula, Graham M. Birtwistle, MacMillan, London, England, 1979]에서 채택하였다. Demos의 목적은 본장에서 논하는 이벤트 주도 시뮬레이션의 종류에 대한 교육을 지원하는 데 있다. 그 책은 이러한 시뮬레이션의 클래스와 Simula에서 그 구현을 철저하게 다루고 있다. 유용한 예제가 많이 실려 있고, 각 예제는 이번 장과 앞장에서 제공한 Smalltalk-80 시뮬레이션 프레임워크의 컨텍스트에서 구현 가능하다. 우리는 Demos 파일 시스템의 변형체와 자동차 운송 선박, 그리고 본 장에 제시된 그림에 대한 정보 시스템 예제를 이용하며, Smalltalk-80 구현으로 접근하는 방법을 익힌 후 관심이 있는 독자는 Birtwistle의 예제를 좀 더 시도해볼 수 있겠다.


파일 시스템 예제에서 "writer" 프로세스는 파일을 업데이트하고, "reader" 프로세스는 그 파일을 읽는다. Reader의 수와 상관없이 동시에 파일로 접근할 수 있지만 writer는 파일로 유일한 접근성을 가져야 한다. 또한 writer의 우선순위가 reader보다 높다. 이벤트의 각 시퀀스는 아래 프로그램에 나타낸다. 예제는 통계를 수집하는 또 다른 방법을 비롯해 자원에 대한 우선순위 큐(priority queueing)의 사용을 설명한다. 이런 경우, 수집된 통계는 쓰기 횟수와 읽기 횟수의 기록에 해당한다.


세 명의 시스템 파일 리더(reader)와 두 명의 파일 라이터(writer)가 있고, 세 개의 (소모 불가) 파일 자원이 있다고 가정하자. 초기화 메서드는 0의 값으로 된 엔트리 두 개, reads와 writes로 이루어진 통계 사전을 명시한다. 시뮬레이션은 시뮬레이트된 시간 단위 25동안 실행되고, 스스로 25 시간에 finishUp 메시지를 수신하도록 스케줄링하였다.


FileSystemReaders와 FileSystemWriters에 값이 주어졌으므로 이벤트 trace에서 식별할 수 있다고 나타나는 defineArrivalSchedule의 구현을 주목하자.

클래스명 FileSystem
슈퍼클래스 Simulation
인스턴스 변수명 statistics
인스턴스 메서드
initialize-release
    initialize
        super initialize.
        statistics  Dictionary new: 2.
        statistics at: #reads put: 0.
        statistics at: #writes put: 0
    defineArrivalSchedule
        self scheduleArrivalOf: (FileSystemReader new label: 'first') at: 0.0.
        self scheduleArrivalOf: (FileSystemWriter new label: 'first') at: 0.0.
        self scheduleArrivalOf: (FileSystemReader new label: 'second') at: 0.0.
        self scheduleArrivalOf: (FileSystemWriter new label: 'second') at: 1.0.
        self scheduleArrivalOf: (FileSystemReader new label: 'third') at: 2.0.
        self schedule: [self finishUp] at: 25
    defineResources
        self produce: 3 of: 'File'

statistics
    statisticsAt: aKey changeBy: anInteger
        statistics at: aKey
            put: (statistics at: aKey) + anInteger
    printStatisticsOn: aStream
        statistics printOn: aStream


FileSystemReader는 5가지 작업의 시퀀스를 반복하여 실행하는데, 이는 하나의 File 자원 획득하기, 파일을 읽기에 충분한 시간만큼 보유하기, 자원 해제하기, 읽기 통계의 기록 업데이트하기, 파일로부터 읽은 정보를 이용하기에 충분한 시간만큼 보유하기가 해당한다. FileSystemWriters는 3개의 파일 자원을 획득하고, 파일에 작성하기 위해 보유하며, 자원을 해제하고, 쓰기 통계를 업데이트한다. FileSystemReader의 우선순위는 1로 설정되며, FileSystemWriter의 우선순위는 2가 된다. 이렇게 소모 불가한 ResourceProvider 'File'은 FileSystemReaders 이전에 FileSystemWriters로 집중할 것이다.


이벤트의 trace를 얻기 위해 EventMonitor의 서브클래스로서 두 개의 시뮬레이션 객체가 생성된다. 각 객체는 그것을 식별하는 역할을 하게 될 단일 라벨을 갖고 있으므로 printOn: aStream에 대한 응답만 재구현된다.

클래스명 FileSystemReader
슈퍼클래스 EventMonitor
인스턴스 메서드
accessing
    label: aString
        label  aString
simulation control
    tasks
        | file |
        "The repeated sequence of tasks is as follows"
        [true]
            whileTrue:
                [file  self acquire: 1 ofResource: 'File' withPriority: 1.
                    self holdFor: 2.0.
                    self release: file.
                    ActiveSimulation statisticsAt: #reads changeBy: 1.
                    self holdFor: 5.0 ]

printing
    printOn: aStream
        aStream nextPutAll: label.
        aStream space.
        self class name printOn: aStream
클래스명 FileSystemWriter
슈퍼클래스 EventMonitor
인스턴스 메서드
accessing
    label: aString
        label  aString

simulation control
    tasks
        | file |
        "The repeated sequence of tasks is as follows"
        [true]
            whileTrue:
                ["Gather information"
                    self holdFor: 5.0.
                    file  self acquire: 3 ofResource: 'File' withPriority: 2.
                    self holdFor: 3.0.
                    self release: file.
                    ActiveSimulation statisticsAt: #writes changeBy: 1]

printing
    printOn: aStream
        aStream nextPutAll: label.
        aStream space.
        self class name printOn: aStream


시뮬레이션이 시간 25에서 스스로 중단할 때까지 5개의 시뮬레이션 객체가 작업을 수행한다. 모델러가 SimulationObject의 작업을 명시할 때는 Smalltalk-80 언어의 모든 제어 구조체를 이용할 수 있다. 이벤트의 trace를 보면 FileSystemWriters의 자원 필요성이 더 크고 우선순위가 높아 FileSystemReaders가 지연되는 것을 확인할 수 있다. 예를 들어, 첫 번째와 두 번째 FileSystemReaders는 7.0 시간에서 지연되고, 세 번째는 9.0 시간에서, 그리고 자원을 필요로 하는 FileSystemWriter가 하나도 없는 시점인 11.0이 될 때까지 모든 FileSystemReaders는 지연된다.

0.0 first FileSystemReader enters
0.0 first FileSystemReader requests 1 of File
0.0 first FileSystemReader obtained 1 of File
0.0 first FileSystemReader holds for 2.0
0.0 first FileSystemWriter enters
0.0 first FileSystemWriter holds for 5.0
0.0 second FileSystemReader enters
0.0 second FileSystemReader requests 1 of File
0.0 second FileSystemReader obtained 1 of File
0.0 second FileSystemReader holds for 2.0
1.0 second FileSystemWriter enters
1.0 second FileSystemWriter holds for 5.0
2.0 second FileSystemReader releases 1 of File
2.0 second FileSystemReader holds for 5.0
2.0 first FileSystemReader releases 1 of File
2.0 first FileSystemReader holds for 5.0
2.0 third FileSystemReader enters
2.0 third FileSystemReader requests 1 of File
2.0 third FileSystemReader obtained 1 of File
2.0 third FileSystemReader holds for 2.0
4.0 third FileSystemReader releases 1 of File
4.0 third FileSystemReader holds for 5.0
5.0 first FileSystemWriter requests 3 of File
5.0 first FileSystemWriter obtained 3 of File
5.0 first FileSystemWriter holds for 3.0
6.0 second FileSystemWriter requests 3 of File
7.0 first FileSystemReader requests 1 of File
7.0 second FileSystemReader requests 1 of File
8.0 first FileSystemWriter releases 3 of File
8.0 first FileSystemWriter holds for 5.0
8.0 second FileSystemWriter obtained 3 of File
8.0 second FileSystemWriter holds for 3.0
9.0 third FileSystemReader requests 1 of File
11.0 second FileSystemWriter releases 3 of File
11.0 second FileSystemWriter holds for 5.0
11.0 first FileSystemReader obtained 1 of File
11.0 first FileSystemReader holds for 2.0
11.0 second FileSystemReader obtained 1 of File
11.0 second FileSystemReader holds for 2.0
11.0 third FileSystemReader obtained 1 of File
11.0 third FileSystemReader holds for 2.0
13.0 third FileSystemReader releases 1 of File
13.0 third FileSystemReader holds for 5.0
13.0 second FileSystemReader releases 1 of File
13.0 second FileSystemReader holds for 5.0
13.0 first FileSystemReader releases 1 of File
13.0 first FileSystemReader holds for 5.0
13.0 first FileSystemWriter requests 3 of File
13.0 first FileSystemWriter obtained 3 of File
13.0 first FileSystemWriter holds for 3.0
16.0 first FileSystemWriter releases 3 of File
16.0 first FileSystemWriter holds for 5.0
16.0 second FileSystemWriter requests 3 of File
16.0 second FileSystemWriter obtained 3 of File
16.0 second FileSystemWriter holds for 3.0
18.0 first FileSystemReader requests 1 of File
18.0 second FileSystemReader requests 1 of File
18.0 third FileSystemReader requests 1 of File
19.0 second FileSystemWriter releases 3 of File
19.0 second FileSystemWriter holds for 5.0
19.0 first FileSystemReader obtained 1 of File
19.0 first FileSystemReader holds for 2.0
19.0 second FileSystemReader obtained 1 of File
19.0 second FileSystemReader holds for 2.0
19.0 third FileSystemReader obtained 1 of File
19.0 third FileSystemReader holds for 2.0
21.0 third FileSystemReader releases 1 of File
21.0 third FileSystemReader holds for 5.0
21.0 second FileSystemReader releases 1 of File
21.0 second FileSystemReader holds for 5.0
21.0 first FileSystemReader releases 1 of File
21.0 first FileSystemReader holds for 5.0
21.0 first FileSystemWriter requests 3 of File
21.0 first FileSystemWriter obtained 3 of File
21.0 first FileSystemWriter holds for 3.0
24.0 first FileSystemWriter releases 3 of File
24.0 first FileSystemWriter holds for 5.0
24.0 second FileSystemWriter requests 3 of File
24.0 second FileSystemWriter obtained 3 of File
24.0 second FileSystemWriter holds for 3.0


이 시점에서 현재 시간은 25이고 수집된 통계는 FileSystem에 printStatisticsOn: aStream 메시지를 전송함으로써 출력되는데, 이 메시지에서 Stream은 가령 FileStream이 될 수 있다. 그 결과는 다음과 같다.

reads 9
writes 5


FileSystemReaders가 자원의 부족이나 낮은 우선순위로 인해 지연되지 않았다면 해당 시간 프레임 동안 읽기 횟수는 12회가 되었을 것임을 주목한다.


재생 가능 자원(Renewable Resources)

생산자/소비자 동기화를 수반하는 시뮬레이션에서 생산자 역할을 하는 시뮬레이션 객체는 소비자 역할을 하는 다른 객체에서 이용 가능한 자원을 만든다. 시뮬레이션은 고정된 수의 자원에서 시작하는데, 아마 0이 될 것이다. 생산자 객체는 이용 가능한 자원의 수를 증가시키고, 소비자 객체는 그러한 자원을 감소시킨다. 이러한 유형의 자원은 이용 가능한 자원의 양에 제한이 없다는 점에서 소모 불가 자원과는 차이가 있다. 그러한 자원을 "renewable"(재생 가능) 자원이라 부른다. 소모 불가 자원에서 한계는 StaticResource를 통한 SimulationObject의 반환 자원에 의해 간접적으로 이루어진다는 점을 주목한다.


자동차 영업소 시뮬레이션은 간단한 재상 가능 자원의 예에 해당한다. 소비자는 2-6일 마다 자동차를 구매하기 위해 영업소를 방문한다. 자동차 판매직원은 현장에 12대의 자동차로 시작하고, 자동차가 판매되면 새로운 자동차가 배달될 때까지 주문을 기다려야 한다. 90일마다 영업소에 12대의 새차가 도착한다. 모든 자동차는 동일하고 모든 손님은 기꺼이 기다리는 것으로 가정하여 자동차를 즉시 이용할 수 없을 경우 판매를 놓치지 않도록 한다. 자동차 판매직원은 훌륭한 서비스를 제공하는 데 관심이 있으면서 재고가 많은 것은 꺼린다. 기다리는 손님의 큐에 대한 평균 길이를 살펴봄으로써 판매직원은 고객 불만족을 최소화하고 새로운 자동차의 재고를 작게 유지하도록 분기별 자동차 주문을 수정할 수 있다.


자동차 구매자가 자동차를 받을 때까지 기다려야 하는 시간에 대한 통계는 시뮬레이션이 보관한다. 사용된 메서드는 23장에서 Museum에 방문하는 Visitors에 관한 정보를 수집할 때 보였던 메서드와 동일하며, Histogram은 CarDearler에 의해 유지된다. 각 CarBuyer는 엔트리 시간을 기억하며, 시뮬레이션을 나갈 때마다 CarBuy가 시뮬레이션에서 소비한 시간 길이가 Histogram에 로그된다. 시간 길이는 CarBuyer가 자동차를 받을 때까지 대기해야 하는 시간량과 동일한데, 자동차를 수령하는 것이 CarBuyer의 유일한 작업이기 때문이다.

클래스명 CarDealer
슈퍼클래스 Simulation
인스턴스 변수명 statistics
인스턴스 메서드
initialize-release
    initialize
        super initialize.
        statistics  Histogram from: 1 to: 365 by: 7
    defineArrivalSchedule
        self scheduleArrivalOf: CarBuyer
            accordingTo: (Uniform from: 2 to: 6)
            startingAt: 1.0
        self scheduleArrivalOf: (CarDelivery new) at: 90.0
        "only one delivery is scheduled; the instance of CarDelivery will reschedule itself as the last of its tasks"
    defineResources
        self produce: 12 of: 'Car'

accessing
    exit: aSimulationObject
        super exit: aSimulationObject.
        "A CarDelivery could be exiting--ignore it"
        (aSimulationObject isKindOf: CarBuyer
            ifTrue: [statistics store: currentTime -- aSimulationObject entryTime]
    printStatistics: aStream
        statistics printStatisticsOn: aStream


모든 CarBuyer는 자동차를 받고 싶어하며, 자동차를 즉시 이용할 수 없다면 CarBuyer는 기다리는 수밖에 없다.

클래스명 CarBuyer
슈퍼클래스 SimulationObject
인스턴스 변수명 entryTime
인스턴스 메서드
accessing
    entryTime
        entryTime

simulation control
    initialize
        super initialize.
        entryTime  ActiveSimulation time
    tasks
        self acquire: 1 ofResource: 'Car'


CarDelivery는 10~12대의 새차를 생산한다. 새차를 생산하고 나면 CarDelivery 객체는 스스로 90일 이내에 반환하도록 스케줄링한다. 이 방법이 아니라면 CarDelivery가 90일 동안 보유한 다음 작업을 반복하도록 구현하는 대안책도 있다.

클래스명 CarDelivery
슈퍼클래스 SimulationObject
인스턴스 메서드
simulation control
    tasks
        "Get access to the Car resource and produce 10, 11, or 12 new cars"
        self produce: ((SampleSpace data: #(10 11 12)) next)
            ofResource: 'Car'.
        "Schedule a new delivery in 90 days"
        ActiveSimulation scheduleArrivalOf: self
            at: ActiveSimulation time + 90


통계는 구매자 수, 구매자의 최소, 최대 그리고 평균 대기 시간, 각 대기 시간 간격마다 구매자의 수를 제공한다. 204일 이상 대기한 사람은 없다. 91명의 자동차 구매자가 판매원을 방문했고, 12명은 판매원에게 자동차가 있었기 때문에 기다릴 필요가 없었다. 기다린 후에 서비스를 제공 받은 43명의 고객은 평균 78.5일 동안 대기하였다.


360.0 시간에서 통계는 다음과 같은 정보를 표시한다.

  객체 수 최소값 최대값 평균값
  55 0.0 197.168 78.5476
엔트리 객체 수 빈도수  
1-8 2 0.0363636 XX  
8-15 3 0.0545454 XXX  
15-22 2 0.0363636 XX  
22-29 1 0.0181818 X  
29-36 2 0.0363636 XX  
36-43 1 0.0181818 X  
43-50 0 0.0    
50-57 1 0.0181818 X  
57-64 2 0.0363636 XX  
64-71 1 0.0181818 X  
71-78 2 0.0363636 XX  
78-85 2 0.0363636 XX  
85-92 2 0.0363636 XX  
92-99 0 0.0    
99-106 0 0.0    
106-113 1 0.0181818 X  
113-120 3 0.0545454 XXX  
120-127 2 0.0363636 XX  
127-134 2 0.0363636 XX  
134-141 2 0.0363636 XX  
141-148 1 0.0181818 X  
148-155 0 0.0    
155-162 1 0.0181818 X  
162-169 2 0.0363636 XX  
169-176 2 0.0363636 XX  
176-183 1 0.0181818 X  
183-190 2 0.0363636 XX  
190-197 2 0.0363636 XX  
197-204 1 0.0181818 X  
204-211 0 0.0    
보류 요청 36명의 구매자가 자동차 대기 중


위의 정보로부터 우리는 배달된 자동차의 수는 소비자 욕구를 충족하기 위해 안전하게 증가, 심지어 배가 될 수도 있다고 추정할 수 있다.


페리 서비스 예제

다음 예제는 Birtwistle 서적에 소개된 예제와 같다. 자동차를 실은 채 섬과 본토를 오가는 페리 셔틀이 예이다. 페리는 7:00 a.m.에 서비스를 시작하고 (일일 중 420분) 정박 위치 중 하나에 도달하면 10:45 p.m.에 멈춘다 (일일 중 1365분). 페리의 자동차 수용량은 6대에 불과하다.


페리의 작업은 6대 이하의 대기 차량을 싣고 수로를 건너는 일이다. 건너는 데에는 0.5 분의 표준 편차로 약 8분이 소요된다. 한 쪽 끝에서 다른 끝으로 건너는 활동은 시간이 1365 분이 될 때까지 계속된다. 다음 소개된 FerrySimulation은 어떤 날의 페리 서비스를 설명한다. Ferry의 정의에서 한쪽에서 다른 끝으로 Ferry를 반복하여 전송하기 위한 Smalltalk-80 whileFalse: 제어 구조체를 주목하고, 작업 설명을 load, holdFor: (cross over), unload, changeSide로 나누기 위해 사용된 메시지도 주목한다.

클래스명 Ferry
슈퍼클래스 SimulationObject
인스턴스 변수명 carsOnBoard
currentSide
인스턴스 메서드
simulation control
    initialize
        super initialize.
        carsOnBoard  0.
        currentSide  'Mainland'
    tasks
        "Initialize the count of loaded cars and then keep loading until at most 6 are on board. Stop loading if no more cars are waiting at the dock."
        [ActiveSimulation time > 1365.0] whileFalse:
            [carsOnBoard  0.
                self load.
                self holdFor: (Normal mean: 8 deviation: 0.5) next.
                self unload.
                self changeSide]
    load
        "It takes 0.5 minutes to load each car. Only try to acquire a resource, a car from this side's dock, if it is there. The conditional for the repetition checks remaining resources and only continues if a car is waiting."
        [carsOnBoard < 6
            and: [self inquirefor: 1 of: currentSide]]
                whileTrue:
                    [self acquire: 1 ofResource: currentSide.
                        self holdFor: 0.5.
                        carsOnBoard  carsOnBoard + 1]
    changeSide
        currentSide  currentSide = 'Mainland'
            ifTrue: ['Island']
            ifFalse: ['Mainland']
    unload
        "It takes 0.5 minutes to unload each car."
        self holdFor: carsOnBoard*0.5.


본토 또는 섬의 정박지점에 도착하는 자동차를 시뮬레이트하기 위해, 즉 이러한 위치에서 자동차를 생성하기 위해서는 두 개의 SimulationObjects가 필요할 것이다.

클래스명 IslandArrival
슈퍼클래스 SimulationObject
인스턴스 메서드
simulation control
    tasks
        self produce: 1 ofResource: 'Island'
클래스명 MainlandArrival
슈퍼클래스 SimulationObject
인스턴스 메서드
simulation control
    tasks
        self produce: 1 ofResource: 'Mainland'


페리 시뮬레이션에는 하나는 본토용 하나는 섬용으로 두 종류의 Resources가 있으며, 도착하는 자동차를 큐로 추가한다. 이러한 자원이 먼저 생성될 때, 즉 요일이 시작될 때는 본토 정박지에 이미 기다리는 차가 세 대가 있고 섬 정박지에서 기다리는 차는 한 대도 없다. 도착 스케줄은 평균적으로 0.15 분마다 자동차가 도착하고 있음을 나타낸다.

클래스명 FerrySimulation
슈퍼클래스 Simulation
인스턴스 메서드
initialization
    defineArrivalSchedule
        self scheduleArrivalOf: MainlandArrival
            accordingTo: (Exponential parameter: 0.15)
            startingAt: 420.0.
        self scheduleArrivalOf: IslandArrival
            accordingTo: (Exponential parameter: 0.15)
            startingAt: 420.0.
        self scheduleArrivalOf: Ferry new at: 420.0
    defineResources
        self produce: 3 of: 'Mainland'
        self produce: 0 of: 'Island'


데이터가 축적되는 동안 시뮬레이션이 수집해야 하는 데이터가 있다. 첫째, Ferry는 총 이동 횟수, 운송하는 승용차 수, 승용차를 싣지 않고 이동하는 횟수를 세어야 한다. 이러한 데이터는 세 가지 인스턴스 변수(trips, totalCars, emptyTrips)를 Ferry 클래스의 정의에서 추가하고 세 가지 메서드를 수정하면 얻을 수 있다.

클래스명 Ferry
슈퍼클래스 SimulationObject
인스턴스 변수명 emptyTrips
carsOnBoard
currentSide
trips
totalCars
emptyTrips
인스턴스 메서드
initialize
    super initialize.
    carsOnBoard  0.
    currentSide  'Mainland'
    trips  0.
    totalCars  0.
    emptyTrips  0 

load
    "Keep a running tally of the cars carried"
    [carsOnBoard < 6 
        and: [self inquireFor: 1 ofResource: currentSide]]
            whileTrue:
                [self acquire: 1 ofResource: currentSide.
                    self holdFor: 0.5.
                    carsOnBoard  carsOnBoard + 1.
                    totalCars  totalCars + 1]

tasks
    "Check for an empty trip and keep a tally of trips"
    [ActiveSimulation time > 1365.0] whileFalse:
        [carsOnBoard  0.
            self load.
            carsOnBoard = 0 ifTrue: [emptyTrips  emptyTrips + 1]. 
            self holdFor: (Normal mean: 8 deviation: 0.5) next.
            self unload.
            self changeSide.
            trips  trips + 1]


또한 본토와 섬 도착지 개수의 최대 크기, 즉 Ferry를 기다리는 최대 큐를 알길 원할 것이다. FerrySimulation은 두 가지 인스턴스 변수, maxMainland와 maxIsland를 추가하여 이러한 정보를 결정할 수 있는데, produce: amount of: resourceName 메시지가 시뮬레이션으로 전송되고 자원의 양이 증가할 때마다 해당하는 변수는 현재 값의 최대와 자원의 최대로 리셋될 수 있다.


우리가 제공하는 trace는 이벤트의 시작과 끝 시퀀스를 보여준다. 섬과 본토에 도착하는 승용차를 Ferry의 반복 작업과 따로 열거하였다.

420.000 IslandArrival 1
420.000 MainlandArrival 1
425.290 MainlandArrival 2
429.380 MainlandArrival 3
430.830 IslandArrival 2
431.302 IslandArrival 3
434.209 IslandArrival 4
438.267 IslandArrival 5
440.864 IslandArrival 6
441.193 MainlandArrival 4
448.044 IslandArrival 7
448.827 IslandArrival 8
453.811 IslandArrival 9
458.804 MainlandArrival 5
467.860 IslandArrival 10
470.800 IslandArrival 11
473.957 MainlandArrival 6
475.508 IslandArrival 12


이는 아래와 같이 계속된다...

1300.87 IslandArrival 169
1301.11 MainlandArrival 124
1301.19 IslandArrival 170
1306.75 IslandArrival 171
1309.30 IslandArrival 172
1315.24 MainlandArrival 125
1319.65 MainlandArrival 126
1321.80 MainlandArrival 127
1322.39 MainlandArrival 128
1328.45 IslandArrival 173
1328.99 IslandArrival 174
1329.77 MainlandArrival 129
1331.63 IslandArrival 175
1335.43 MainlandArrival 130
1338.93 IslandArrival 176
1342.46 MainlandArrival 131
1348.11 IslandArrival 177
1358.63 MainlandArrival 132
1359.10 IslandArrival 178
1360.79 MainlandArrival 133


Ferry는 세 대의 차가 기다리는 본토에서 시작하며 섬에는 아직 기다리는 자동차가 없다. 자동차는 곧 각 위치에 도착한다.

420,0 Ferry 1 enters 본토에서 적재: 본토에 4대, 섬에 1대의 자동차가 대기
420.0 Ferry 1 obtained 1 of Mainland, holds for 0.5
420.5 Ferry 1 obtained 1 of Mainland, holds for 0.5
421.0 Ferry 1 obtained 1 of Mainland, holds for 0.5
421.5 Ferry 1 obtained 1 of Mainland, holds for 0.5 교차
422.0 Ferry 1 holds for 8.56369 섬에 4대의 자동차 하선: 본토에 2대, 섬에 1대의 자동차가 대기
430.564 Ferry 1 holds for 2.0 섬에서 적재: 본토에 2대, 섬에 3대의 자동차가 대기
432.564 Ferry 1 obtained 1 of Island, holds for 0.5
433.064 Ferry 1 obtained 1 of Island, holds for 0.5
433.564 Ferry 1 obtained 1 of Island, holds for 0.5 교차
434.064 Ferry 1 holds for 8.55344 본토에 3대의 자동차 하선: 본토에 3대, 섬에 3대의 자동차가 대기
442.617 Ferry 1 holds for 1.5 본토에서 적재: 본토에 3대, 섬에 0대의 자동차가 대기
444.117 Ferry 1 obtained 1 of Mainland, holds for 0.5
444.617 Ferry 1 obtained 1 of Mainland, holds for 0.5
445.117 Ferry 1 obtained 1 of Mainland, holds for 0.5 교차
445.617 Ferry 1 holds for 8.98081 섬에서 3대의 자동차 하선: 본토에 0대, 섬에 6대의 자동차가 대기
454.598 Ferry 1 holds for 1.5 섬에서 적재: 본토에 0대, 섬에 6대의 자동차가 대기
456.098 Ferry 1 obtained 1 of Island, holds for 0.5
456.598 Ferry 1 obtained 1 of Island, holds for 0.5
457.098 Ferry 1 obtained 1 of Island, holds for 0.5
457.598 Ferry 1 obtained 1 of Island, holds for 0.5
458.098 Ferry 1 obtained 1 of Island, holds for 0.5
458.598 Ferry 1 obtained 1 of Island, holds for 0.5 교차
459.098 Ferry 1 holds for 7.96448 본토에 6대의 자동차 하선: 본토에 1대, 섬에 0대의 자동차 대기
467.062 Ferry 1 holds for 3.0 본토에서 적재: 본토에 1대, 섬에 1대의 자동차 대기
470.062 Ferry 1 obtained 1 of Mainland, holds for 0.5 섬에서 적재할 때까지 계속
1299.52 Ferry 1 obtained 1 of Island, holds for 0.5
1300.02 Ferry 1 obtained 1 of Island, holds for 0.5 교차
1300.52 Ferry 1 holds for 7.23914 본토에 2대의 자동차 하선
1307.76 Ferry 1 holds for 1.0 본토에서 적재: 본토에 1대, 섬에 3대의 자동차 대기
1308.76 Ferry 1 obtained 1 of Mainland, holds for 0.5 교차
1309.26 Ferry 1 holds for 7.78433 섬에서 적재: 본토에 2대, 섬에 4대의 자동차 대기
1317.54 Ferry 1 obtained 1 of Island, holds for 0.5
1318.04 Ferry 1 obtained 1 of Island, holds for 0.5
1318.54 Ferry 1 obtained 1 of Island, holds for 0.5
1319.04 Ferry 1 obtained 1 of Island, holds for 0.5 교차
1319.54 Ferry 1 holds for 8.51123 본토에서 4대의 자동차 하선: 본토에 3대, 섬에 0대의 자동차 대기
1328.05 Ferry 1 holds for 2.0 본토에서 적재: 본토에 5대, 섬에 2대의 자동차 대기
1330.05 Ferry 1 obtained 1 of Mainland, holds for 0.5
1330.55 Ferry 1 obtained 1 of Mainland, holds for 0.5
1331.05 Ferry 1 obtained 1 of Mainland, holds for 0.5
1331.55 Ferry 1 obtained 1 of Mainland, holds for 0.5
1332.05 Ferry 1 obtained 1 of Mainland, holds for 0.5 교차
1332.55 Ferry 1 holds for 8.17247 섬에 5대의 자동차 하선: 본토에 1대, 섬에 4대의 자동차 대기
1340.72 Ferry 1 holds for 2.5 섬에서 적재: 본토에 2대, 섬에 4대의 자동차 대기
1343.22 Ferry 1 obtained 1 of Island, holds for 0.5
1343.72 Ferry 1 obtained 1 of Island, holds for 0.5
1344.22 Ferry 1 obtained 1 of Island, holds for 0.5
1344.72 Ferry 1 obtained 1 of Island, holds for 0.5 교차
1345.22 Ferry 1 holds for 7.75318 본토에서 하선: 본토에 2대, 섬에 1대의 자동차 대기
1352.98 Ferry 1 holds for 2.0 본토에서 적재: 본토에 2대, 섬에 1대의 자동차 대기
1354.98 Ferry 1 obtained 1 of Mainland, holds for 0.5
1355.48 Ferry 1 obtained 1 of Mainland, holds for 0.5 교차
1355.98 Ferry 1 holds for 8.54321 섬에서 2대의 자동차 하선: 본토에 2대, 섬에 2대의 자동차 대기
1364.52 Ferry 1 holds for 1.0 종료 시간
1365.52 Ferry 1 exits


수집한 데이터를 보면 Ferry는 총 79회 이동, 310대의 자동차를 이동(1회 이동마다 평균 3.9대)하였다. 한 대도 적재하지 않고 이동한 횟수는 한 번도 없었다. 본토의 대기선은 최대 7대의 자동차인 반면 섬에서는 최대 18대가 대기하였다. Ferry를 닫는 시간에는 두 대의 자동차가 각 위치에 남는다.


Notes