Smalltalk80LanguageImplementationKor:Chapter 24
- 제 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를 닫는 시간에는 두 대의 자동차가 각 위치에 남는다.