Updated dining-philosophers.md

heejongahn authored
revision 874724772a7b50086eb8ca0f9a3d1b7a7fd718ab
dining-philosophers
# 3.2. 철학자들의 만찬 (Dining Philosophers) - 0%

For our second project, let’s look at a classic concurrency problem. It’s
called ‘the dining philosophers’. It was originally conceived by Dijkstra in
1965, but we’ll use the version from [this paper][paper] by Tony Hoare in 1985.

[paper]: http://www.usingcsp.com/cspbook.pdf

> In ancient times, a wealthy philanthropist endowed a College to accommodate
> five eminent philosophers. Each philosopher had a room in which he could
> engage in his professional activity of thinking; there was also a common
> dining room, furnished with a circular table, surrounded by five chairs, each
> labelled by the name of the philosopher who was to sit in it. They sat
> anticlockwise around the table. To the left of each philosopher there was
> laid a golden fork, and in the centre stood a large bowl of spaghetti, which
> was constantly replenished. A philosopher was expected to spend most of his
> time thinking; but when he felt hungry, he went to the dining room, sat down
> in his own chair, picked up his own fork on his left, and plunged it into the
> spaghetti. But such is the tangled nature of spaghetti that a second fork is
> required to carry it to the mouth. The philosopher therefore had also to pick
> up the fork on his right. When we was finished he would put down both his
> forks, get up from his chair, and continue thinking. Of course, a fork can be
> used by only one philosopher at a time. If the other philosopher wants it, he
> just has to wait until the fork is available again.

This classic problem shows off a few different elements of concurrency. The
reason is that it's actually slightly tricky to implement: a simple
implementation can deadlock. For example, let's consider a simple algorithm
that would solve this problem:

1. A philosopher picks up the fork on their left.
2. They then pick up the fork on their right.
3. They eat.
4. They return the forks.

Now, let’s imagine this sequence of events
우리의 두 번째 프로젝트를 위해, 고전적인 동시성 문제를 살펴봅시다. '철학자들의 만찬'이라 불리는 문제인데요. 이 문제는 다익스트라_Dijkstra_에 의해 1965년 처음으로 고안되었지만, 우리는 1985년 토니 호어의 [이 논문][paper]에 실린 버전을 사용할 것입니다.

[paper]: http://www.usingcsp.com/cspbook.pdf


> 고대, 어떤 부유한 자선사업가가 다섯 명의 저명한 철학가를 모실 수 있도록 한 대학을 후원했습니다.
> 각각의 철학자들은 그가 그의 전문적인 사고에 몰입할 수 있는 방을 하나씩 얻었죠. 그리고 공동으로
> 사용하는 식당이 하나 있었는데, 그 식당에는 각 철학자들의 이름이 새겨진 다섯 의자로 둘러쌓인
> 원형 식탁이 있었습니다. 철학자들은 이 식닥을 둘러싸고 반시계 방향으로 앉았습니다. 각 철학자의
> 왼쪽에는 금으로 된 포크가 하나씩 놓여 있었고, 식탁의 중앙에는 계속해서 채워지는, 스파게티가
> 가득 담긴 쟁반이 놓여 있었죠. 대학에서는 철학자라는 이들은 어차피 대부분의 시간을 그저
> 생각하는데에 사용했습니다. 하지만, 배고픔을 느낄 때에는 중앙의 식당으로 와서, 자기 이름이
> 쓰인 의자에 앉은 뒤에, 그의 왼쪽에 놓인 그의 포크를 집어들고, 스파게티에 그걸 내리꽂았죠.
> 하지만 스파게티란게 워낙에 꼬여 있는 음식인지라, 스파게티를 입으로 옮기기 위해서는 포크가
> 하나 더 필요했어요. 따라서 그 철학자는 그의 오른쪽에 놓인 포크를 집어들어야만 했죠. 배를
> 채운 뒤, 그 철학자는 양 손에 든 포크를 내려놓고, 의자에서 일어나, 다시 생각을 이어가겠죠.
> 당연히 하나의 포크는 한 번에 한 명만 사용 가능합니다. 만약 다른 철학자가 현재 사용중인
> 포크를 필요로 한다면, 지금 그 포크를 사용중인 철학자가 식사를 끝낼 때까지 기다려야해요.

이 고전적인 문제는 동시성의 몇 가지 요소들을 보여줍니다. 사실 이 문제를 구현하는 것은 좀
까다로운데, 간단한 구현에선 데드락이 일어날 수 있어요. 예를 들어, 이 문제를 해결 할 수 있는
다음 간단한 알고리즘을 생각해봅시다.

1. 철학자가 자신의 왼쪽에 놓인 포크를 집어든다.
2. 그 다음엔 자기 오른쪽에 놓인 포크를 집어든다.
3. 먹는다.
4. 포크를 반납한다.

이제, 다음과 같은 일련의 사건들을 상상해보세요
:

1. Philosopher 1 begins the algorithm, picking up the fork on their left.
2. Philosopher 2 begins the algorithm, picking up the fork on their left.
3. Philosopher 3 begins the algorithm, picking up the fork on their left.
4. Philosopher 4 begins the algorithm, picking up the fork on their left.
5. Philosopher 5 begins the algorithm, picking up the fork on their left.
6. ... ? All the forks are taken, but nobody can eat!

1. 철학자 1이 알고리즘을 시작하고, 자기 왼쪽에 놓인 포크를 집어듭니다.
2. 철학자 2가 알고리즘을 시작하고, 자기 왼쪽에 놓인 포크를 집어듭니다.
3. 철학자 3이 알고리즘을 시작하고, 자기 왼쪽에 놓인 포크를 집어듭니다.
4. 철학자 4가 알고리즘을 시작하고, 자기 왼쪽에 놓인 포크를 집어듭니다.
5. 철학자 5가 알고리즘을 시작하고, 자기 왼쪽에 놓인 포크를 집어듭니다.
6. ...?
모든 포크는 사용중이지만, 아무도 먹을 수 없죠!

There are different ways to solve this problem. We’ll get to our solution in
the tutorial itself. For now, let’s get started modelling the problem itself.
We’ll start with the philosophers:

```rust
struct Philosopher {
name: String,
}

impl Philosopher {
fn new(name: &str) -> Philosopher {
Philosopher {
name: name.to_string(),
}
}
}

fn main() {
let p1 = Philosopher::new("Baruch Spinoza");
let p2 = Philosopher::new("Gilles Deleuze");
let p3 = Philosopher::new("Karl Marx");
let p4 = Philosopher::new("Friedrich Nietzsche");
let p5 = Philosopher::new("Michel Foucault");
}
```

Here, we make a [`struct`][struct] to represent a philosopher. For now,
a name is all we need. We choose the [`String`][string] type for the name,
rather than `&str`. Generally speaking, working with a type which owns its
data is easier than working with one that uses references.

[struct] : struct.html
[string] : string.html


Let’s continue:

```rust
# struct Philosopher {
# name: String,
# }
impl Philosopher {
fn new(name: &str) -> Philosopher {
Philosopher {
name: name.to_string(),
}
}
}
```

This `impl` block lets us define things on `Philosopher` structs. In this case,
we define an ‘associated function’ called `new`. The first line looks like this:

```rust
# struct Philosopher {
# name: String,
# }
# impl Philosopher {
fn new(name: &str) -> Philosopher {
# Philosopher {
# name: name.to_string(),
# }
# }
# }
```

We take one argument, a `name`, of type `&str`. This is a reference to another
string. It returns an instance of our `Philosopher` struct.

```rust
# struct Philosopher {
# name: String,
# }
# impl Philosopher {
# fn new(name: &str) -> Philosopher {
Philosopher {
name: name.to_string(),
}
# }
# }
```

This creates a new `Philosopher`, and sets its `name` to our `name` argument.
Not just the argument itself, though, as we call `.to_string()` on it. This
will create a copy of the string that our `&str` points to, and give us a new
`String`, which is the type of the `name` field of `Philosopher`.

Why not accept a `String` directly? It’s nicer to call. If we took a `String`,
but our caller had a `&str`, they’d have to call this method themselves. The
downside of this flexibility is that we _always_ make a copy. For this small
program, that’s not particularly important, as we know we’ll just be using
short strings anyway.

One last thing you’ll notice: we just define a `Philosopher`, and seemingly
don’t do anything with it. Rust is an ‘expression based’ language, which means
that almost everything in Rust is an expression which returns a value. This is
true of functions as well, the last expression is automatically returned. Since
we create a new `Philosopher` as the last expression of this function, we end
up returning it.

This name, `new()`, isn’t anything special to Rust, but it is a convention for
functions that create new instances of structs. Before we talk about why, let’s
look at `main()` again:

```rust
# struct Philosopher {
# name: String,
# }
#
# impl Philosopher {
# fn new(name: &str) -> Philosopher {
# Philosopher {
# name: name.to_string(),
# }
# }
# }
#
fn main() {
let p1 = Philosopher::new("Baruch Spinoza");
let p2 = Philosopher::new("Gilles Deleuze");
let p3 = Philosopher::new("Karl Marx");
let p4 = Philosopher::new("Friedrich Nietzsche");
let p5 = Philosopher::new("Michel Foucault");
}
```

Here, we create five variable bindings with five new philosophers. These are my
favorite five, but you can substitute anyone you want. If we _didn’t_ define
that `new()` function, it would look like this:

```rust
# struct Philosopher {
# name: String,
# }
fn main() {
let p1 = Philosopher { name: "Baruch Spinoza".to_string() };
let p2 = Philosopher { name: "Gilles Deleuze".to_string() };
let p3 = Philosopher { name: "Karl Marx".to_string() };
let p4 = Philosopher { name: "Friedrich Nietzche".to_string() };
let p5 = Philosopher { name: "Michel Foucault".to_string() };
}
```

That’s much noisier. Using `new` has other advantages too, but even in
this simple case, it ends up being nicer to use.

Now that we’ve got the basics in place, there’s a number of ways that we can
tackle the broader problem here. I like to start from the end first: let’s
set up a way for each philosopher to finish eating. As a tiny step, let’s make
a method, and then loop through all the philosophers, calling it:

```rust
struct Philosopher {
name: String,
}

impl Philosopher {
fn new(name: &str) -> Philosopher {
Philosopher {
name: name.to_string(),
}
}

fn eat(&self) {
println!("{} is done eating.", self.name);
}
}

fn main() {
let philosophers = vec![
Philosopher::new("Baruch Spinoza"),
Philosopher::new("Gilles Deleuze"),
Philosopher::new("Karl Marx"),
Philosopher::new("Friedrich Nietzsche"),
Philosopher::new("Michel Foucault"),
];

for p in &philosophers {
p.eat();
}
}
```

Let’s look at `main()` first. Rather than have five individual variable
bindings for our philosophers, we make a `Vec` of them instead. `Vec` is
also called a ‘vector’, and it’s a growable array type. We then use a
[`for`][for] loop to iterate through the vector, getting a reference to each
philosopher in turn.

[for]: for-loops.html

In the body of the loop, we call `p.eat()`, which is defined above:

```rust,ignore
fn eat(&self) {
println!("{} is done eating.", self.name);
}
```

In Rust, methods take an explicit `self` parameter. That’s why `eat()` is a
method, but `new` is an associated function: `new()` has no `self`. For our
first version of `eat()`, we just print out the name of the philosopher, and
mention they’re done eating. Running this program should give you the following
output:

```text
Baruch Spinoza is done eating.
Gilles Deleuze is done eating.
Karl Marx is done eating.
Friedrich Nietzsche is done eating.
Michel Foucault is done eating.
```

Easy enough, they’re all done! We haven’t actually implemented the real problem
yet, though, so we’re not done yet!

Next, we want to make our philosophers not just finish eating, but actually
eat. Here’s the next version:

```rust
use std::thread;

struct Philosopher {
name: String,
}

impl Philosopher {
fn new(name: &str) -> Philosopher {
Philosopher {
name: name.to_string(),
}
}

fn eat(&self) {
println!("{} is eating.", self.name);

thread::sleep_ms(1000);

println!("{} is done eating.", self.name);
}
}

fn main() {
let philosophers = vec![
Philosopher::new("Baruch Spinoza"),
Philosopher::new("Gilles Deleuze"),
Philosopher::new("Karl Marx"),
Philosopher::new("Friedrich Nietzsche"),
Philosopher::new("Michel Foucault"),
];

for p in &philosophers {
p.eat();
}
}
```

Just a few changes. Let’s break it down.

```rust,ignore
use std::thread;
```

`use` brings names into scope. We’re going to start using the `thread` module
from the standard library, and so we need to `use` it.

```rust,ignore
fn eat(&self) {
println!("{} is eating.", self.name);

thread::sleep_ms(1000);

println!("{} is done eating.", self.name);
}
```

We now print out two messages, with a `sleep_ms()` in the middle. This will
simulate the time it takes a philosopher to eat.

If you run this program, You should see each philosopher eat in turn:

```text
Baruch Spinoza is eating.
Baruch Spinoza is done eating.
Gilles Deleuze is eating.
Gilles Deleuze is done eating.
Karl Marx is eating.
Karl Marx is done eating.
Friedrich Nietzsche is eating.
Friedrich Nietzsche is done eating.
Michel Foucault is eating.
Michel Foucault is done eating.
```

Excellent! We’re getting there. There’s just one problem: we aren’t actually
operating in a concurrent fashion, which is a core part of the problem!

To make our philosophers eat concurrently, we need to make a small change.
Here’s the next iteration:

```rust
use std::thread;

struct Philosopher {
name: String,
}

impl Philosopher {
fn new(name: &str) -> Philosopher {
Philosopher {
name: name.to_string(),
}
}

fn eat(&self) {
println!("{} is eating.", self.name);

thread::sleep_ms(1000);

println!("{} is done eating.", self.name);
}
}

fn main() {
let philosophers = vec![
Philosopher::new("Baruch Spinoza"),
Philosopher::new("Gilles Deleuze"),
Philosopher::new("Karl Marx"),
Philosopher::new("Friedrich Nietzsche"),
Philosopher::new("Michel Foucault"),
];

let handles: Vec<_> = philosophers.into_iter().map(|p| {
thread::spawn(move || {
p.eat();
})
}).collect();

for h in handles {
h.join().unwrap();
}
}
```

All we’ve done is change the loop in `main()`, and added a second one! Here’s the
first change:

```rust,ignore
let handles: Vec<_> = philosophers.into_iter().map(|p| {
thread::spawn(move || {
p.eat();
})
}).collect();
```

While this is only five lines, they’re a dense four. Let’s break it down.

```rust,ignore
let handles: Vec<_> =
```

We introduce a new binding, called `handles`. We’ve given it this name because
we are going to make some new threads, and that will return some handles to those
threads that let us control their operation. We need to explicitly annotate
the type here, though, due to an issue we’ll talk about later. The `_` is
a type placeholder. We’re saying “`handles` is a vector of something, but you
can figure out what that something is, Rust.”

```rust,ignore
philosophers.into_iter().map(|p| {
```

We take our list of philosophers and call `into_iter()` on it. This creates an
iterator that takes ownership of each philosopher. We need to do this to pass
them to our threads. We take that iterator and call `map` on it, which takes a
closure as an argument and calls that closure on each element in turn.

```rust,ignore
thread::spawn(move || {
p.eat();
})
```

Here’s where the concurrency happens. The `thread::spawn` function takes a closure
as an argument and executes that closure in a new thread. This closure needs
an extra annotation, `move`, to indicate that the closure is going to take
ownership of the values it’s capturing. Primarily, the `p` variable of the
`map` function.

Inside the thread, all we do is call `eat()` on `p`.

```rust,ignore
}).collect();
```

Finally, we take the result of all those `map` calls and collect them up.
`collect()` will make them into a collection of some kind, which is why we
needed to annotate the return type: we want a `Vec`. The elements are the
return values of the `thread::spawn` calls, which are handles to those threads.
Whew!

```rust,ignore
for h in handles {
h.join().unwrap();
}
```

At the end of `main()`, we loop through the handles and call `join()` on them,
which blocks execution until the thread has completed execution. This ensures
that the threads complete their work before the program exits.

If you run this program, you’ll see that the philosophers eat out of order!
We have mult-threading!

```text
Gilles Deleuze is eating.
Gilles Deleuze is done eating.
Friedrich Nietzsche is eating.
Friedrich Nietzsche is done eating.
Michel Foucault is eating.
Baruch Spinoza is eating.
Baruch Spinoza is done eating.
Karl Marx is eating.
Karl Marx is done eating.
Michel Foucault is done eating.
```

But what about the forks? We haven’t modeled them at all yet.

To do that, let’s make a new `struct`:

```rust
use std::sync::Mutex;

struct Table {
forks: Vec>,
}
```

This `Table` has an vector of `Mutex`es. A mutex is a way to control
concurrency: only one thread can access the contents at once. This is exactly
the property we need with our forks. We use an empty tuple, `()`, inside the
mutex, since we’re not actually going to use the value, just hold onto it.

Let’s modify the program to use the `Table`:

```rust
use std::thread;
use std::sync::{Mutex, Arc};

struct Philosopher {
name: String,
left: usize,
right: usize,
}

impl Philosopher {
fn new(name: &str, left: usize, right: usize) -> Philosopher {
Philosopher {
name: name.to_string(),
left: left,
right: right,
}
}

fn eat(&self, table: &Table) {
let _left = table.forks[self.left].lock().unwrap();
let _right = table.forks[self.right].lock().unwrap();

println!("{} is eating.", self.name);

thread::sleep_ms(1000);

println!("{} is done eating.", self.name);
}
}

struct Table {
forks: Vec>,
}

fn main() {
let table = Arc::new(Table { forks: vec![
Mutex::new(()),
Mutex::new(()),
Mutex::new(()),
Mutex::new(()),
Mutex::new(()),
]});

let philosophers = vec![
Philosopher::new("Baruch Spinoza", 0, 1),
Philosopher::new("Gilles Deleuze", 1, 2),
Philosopher::new("Karl Marx", 2, 3),
Philosopher::new("Friedrich Nietzsche", 3, 4),
Philosopher::new("Michel Foucault", 0, 4),
];

let handles: Vec<_> = philosophers.into_iter().map(|p| {
let table = table.clone();

thread::spawn(move || {
p.eat(&table);
})
}).collect();

for h in handles {
h.join().unwrap();
}
}
```

Lots of changes! However, with this iteration, we’ve got a working program.
Let’s go over the details:

```rust,ignore
use std::sync::{Mutex, Arc};
```

We’re going to use another structure from the `std::sync` package: `Arc`.
We’ll talk more about it when we use it.

```rust,ignore
struct Philosopher {
name: String,
left: usize,
right: usize,
}
```

We need to add two more fields to our `Philosopher`. Each philosopher is going
to have two forks: the one on their left, and the one on their right.
We’ll use the `usize` type to indicate them, as it’s the type that you index
vectors with. These two values will be the indexes into the `forks` our `Table`
has.

```rust,ignore
fn new(name: &str, left: usize, right: usize) -> Philosopher {
Philosopher {
name: name.to_string(),
left: left,
right: right,
}
}
```

We now need to construct those `left` and `right` values, so we add them to
`new()`.

```rust,ignore
fn eat(&self, table: &Table) {
let _left = table.forks[self.left].lock().unwrap();
let _right = table.forks[self.right].lock().unwrap();

println!("{} is eating.", self.name);

thread::sleep_ms(1000);

println!("{} is done eating.", self.name);
}
```

We have two new lines. We’ve also added an argument, `table`. We access the
`Table`’s list of forks, and then use `self.left` and `self.right` to access
the fork at that particular index. That gives us access to the `Mutex` at that
index, and we call `lock()` on it. If the mutex is currently being accessed by
someone else, we’ll block until it becomes available.

The call to `lock()` might fail, and if it does, we want to crash. In this
case, the error that could happen is that the mutex is [‘poisoned’][poison],
which is what happens when the thread panics while the lock is held. Since this
shouldn’t happen, we just use `unwrap()`.

[poison]: ../std/sync/struct.Mutex.html#poisoning

One other odd thing about these lines: we’ve named the results `_left` and
`_right`. What’s up with that underscore? Well, we aren’t planning on
_using_ the value inside the lock. We just want to acquire it. As such,
Rust will warn us that we never use the value. By using the underscore,
we tell Rust that this is what we intended, and it won’t throw a warning.

What about releasing the lock? Well, that will happen when `_left` and
`_right` go out of scope, automatically.

```rust,ignore
let table = Arc::new(Table { forks: vec![
Mutex::new(()),
Mutex::new(()),
Mutex::new(()),
Mutex::new(()),
Mutex::new(()),
]});
```

Next, in `main()`, we make a new `Table` and wrap it in an `Arc`.
‘arc’ stands for ‘atomic reference count’, and we need that to share
our `Table` across multiple threads. As we share it, the reference
count will go up, and when each thread ends, it will go back down.


```rust,ignore
let philosophers = vec![
Philosopher::new("Baruch Spinoza", 0, 1),
Philosopher::new("Gilles Deleuze", 1, 2),
Philosopher::new("Karl Marx", 2, 3),
Philosopher::new("Friedrich Nietzsche", 3, 4),
Philosopher::new("Michel Foucault", 0, 4),
];
```

We need to pass in our `left` and `right` values to the constructors for our
`Philosopher`s. But there’s one more detail here, and it’s _very_ important. If
you look at the pattern, it’s all consistent until the very end. Monsieur
Foucault should have `4, 0` as arguments, but instead, has `0, 4`. This is what
prevents deadlock, actually: one of our philosophers is left handed! This is
one way to solve the problem, and in my opinion, it’s the simplest.

```rust,ignore
let handles: Vec<_> = philosophers.into_iter().map(|p| {
let table = table.clone();

thread::spawn(move || {
p.eat(&table);
})
}).collect();
```

Finally, inside of our `map()`/`collect()` loop, we call `table.clone()`. The
`clone()` method on `Arc` is what bumps up the reference count, and when it
goes out of scope, it decrements the count. You’ll notice we can introduce a
new binding to `table` here, and it will shadow the old one. This is often used
so that you don’t need to come up with two unique names.

With this, our program works! Only two philosophers can eat at any one time,
and so you’ll get some output like this:

```text
Gilles Deleuze is eating.
Friedrich Nietzsche is eating.
Friedrich Nietzsche is done eating.
Gilles Deleuze is done eating.
Baruch Spinoza is eating.
Karl Marx is eating.
Baruch Spinoza is done eating.
Michel Foucault is eating.
Karl Marx is done eating.
Michel Foucault is done eating.
```

Congrats! You’ve implemented a classic concurrency problem in Rust.