File: process_interaction.rst

package info (click to toggle)
python-simpy3 3.0.11-3
  • links: PTS, VCS
  • area: main
  • in suites: bookworm
  • size: 1,080 kB
  • sloc: python: 2,885; makefile: 138
file content (146 lines) | stat: -rw-r--r-- 5,725 bytes parent folder | download | duplicates (3)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
===================
Process Interaction
===================

.. currentmodule:: simpy.core

The :class:`~simpy.events.Process` instance that is returned by
:meth:`Environment.process()` can be utilized for process interactions. The two
most common examples for this are to wait for another process to finish and to
interrupt another process while it is waiting for an event.


Waiting for a Process
=====================

As it happens, a SimPy :class:`~simpy.events.Process` can be used like an event
(technically, a process actually *is* an event). If you yield it, you are
resumed once the process has finished. Imagine a car-wash simulation where cars
enter the car-wash and wait for the washing process to finish. Or an airport
simulation where passengers have to wait until a security check finishes.

Lets assume that the car from our last example magically became an electric
vehicle. Electric vehicles usually take a lot of time charging their batteries
after a trip. They have to wait until their battery is charged before they can
start driving again.

We can model this with an additional ``charge()`` process for our car.
Therefore, we refactor our car to be a class with two process methods:
``run()`` (which is the original ``car()`` process function) and ``charge()``.

The ``run`` process is automatically started when ``Car`` is instantiated.
A new ``charge`` process is started every time the vehicle starts parking. By
yielding the :class:`~simpy.events.Process` instance that
:meth:`Environment.process()` returns, the ``run`` process starts waiting for
it to finish::

    >>> class Car(object):
    ...     def __init__(self, env):
    ...         self.env = env
    ...         # Start the run process everytime an instance is created.
    ...         self.action = env.process(self.run())
    ...
    ...     def run(self):
    ...         while True:
    ...             print('Start parking and charging at %d' % self.env.now)
    ...             charge_duration = 5
    ...             # We yield the process that process() returns
    ...             # to wait for it to finish
    ...             yield self.env.process(self.charge(charge_duration))
    ...
    ...             # The charge process has finished and
    ...             # we can start driving again.
    ...             print('Start driving at %d' % self.env.now)
    ...             trip_duration = 2
    ...             yield self.env.timeout(trip_duration)
    ...
    ...     def charge(self, duration):
    ...         yield self.env.timeout(duration)

Starting the simulation is straightforward again: We create an environment,
one (or more) cars and finally call :meth:`~simpy.core.Environment.run()`.

::

    >>> import simpy
    >>> env = simpy.Environment()
    >>> car = Car(env)
    >>> env.run(until=15)
    Start parking and charging at 0
    Start driving at 5
    Start parking and charging at 7
    Start driving at 12
    Start parking and charging at 14


Interrupting Another Process
============================

Imagine, you don't want to wait until your electric vehicle is fully charged
but want to interrupt the charging process and just start driving instead.

SimPy allows you to interrupt a running process by calling its
:meth:`~simpy.events.Process.interrupt()` method::

    >>> def driver(env, car):
    ...     yield env.timeout(3)
    ...     car.action.interrupt()

The ``driver`` process has a reference to the car's ``action`` process. After
waiting for 3 time steps, it interrupts that process.

Interrupts are thrown into process functions as
:exc:`~simpy.exceptions.Interrupt` exceptions that can (should) be handled by
the interrupted process. The process can then decide what to do next (e.g.,
continuing to wait for the original event or yielding a new event)::

    >>> class Car(object):
    ...     def __init__(self, env):
    ...         self.env = env
    ...         self.action = env.process(self.run())
    ...
    ...     def run(self):
    ...         while True:
    ...             print('Start parking and charging at %d' % self.env.now)
    ...             charge_duration = 5
    ...             # We may get interrupted while charging the battery
    ...             try:
    ...                 yield self.env.process(self.charge(charge_duration))
    ...             except simpy.Interrupt:
    ...                 # When we received an interrupt, we stop charging and
    ...                 # switch to the "driving" state
    ...                 print('Was interrupted. Hope, the battery is full enough ...')
    ...
    ...             print('Start driving at %d' % self.env.now)
    ...             trip_duration = 2
    ...             yield self.env.timeout(trip_duration)
    ...
    ...     def charge(self, duration):
    ...         yield self.env.timeout(duration)

When you compare the output of this simulation with the previous example,
you'll notice that the car now starts driving at time ``3`` instead of ``5``::

    >>> env = simpy.Environment()
    >>> car = Car(env)
    >>> env.process(driver(env, car))
    <Process(driver) object at 0x...>
    >>> env.run(until=15)
    Start parking and charging at 0
    Was interrupted. Hope, the battery is full enough ...
    Start driving at 3
    Start parking and charging at 5
    Start driving at 10
    Start parking and charging at 12


What's Next
===========

We just demonstrated two basic methods for process interactions---waiting for
a process and interrupting a process. Take a look at the
:doc:`../topical_guides/index` or the :class:`~simpy.events.Process` API
reference for more details.

In the :doc:`next section <shared_resources>` we will cover the basic usage of
shared resources.