1 of 40

Philosophers

理解するためのスライド

作成者:nfukada(nafuka11)

2 of 40

おしながき

  1. Philosophersはどんな課題か
  2. 並行/並列
  3. 排他制御
  4. Philosophersの実装

2

3 of 40

Philosophersとはどんな課題か

3

4 of 40

Philosophersとはどんな課題か

食事する哲学者の問題」に似た問題

4

5 of 40

食事する哲学者の問題

円卓に哲学者が座り�食事 → 思考を繰り返す

食事には2本のフォークが必要

フォークは1本ずつしか取れない

食事と思考を続けられるか?

Benjamin D. Esham / Wikimedia Commons, CC 表示-継承 3.0, https://commons.wikimedia.org/w/index.php?curid=56559による

5

6 of 40

食事する哲学者の問題

食事と思考を続けられないパターン

Benjamin D. Esham / Wikimedia Commons, CC 表示-継承 3.0, https://commons.wikimedia.org/w/index.php?curid=56559による

6

7 of 40

42のPhilosophers:ルール

食事 → 睡眠 → 思考

  • 食事:2本のフォークを持って食事する
  • 睡眠:フォークを落として眠る
  • 思考:フォークを手に取る

7

8 of 40

42のPhilosophers:引数

引数で以下の情報を与える

  • 哲学者の人数 (=フォークの数)
  • 死ぬまでの時間 (ms)
  • 食事する時間 (ms)
  • 睡眠する時間 (ms)

オプション引数

  • プログラムを終了する食事回数

例:プログラム名 3 600 300 400

8

9 of 40

42のPhilosophers:哲学者の死

シミュレーション開始時間 or�食事開始時間からの�経過時間が死ぬ時間を超えると�哲学者は死ぬ

哲学者が死ぬと�シミュレーションは終了

哲学者が死ぬ例

9

10 of 40

42のPhilosophers:

ログ出力

タイムスタンプ�哲学者の番号�行動

を標準出力に出力

# ログ出力例

# 3人の哲学者

# 死ぬ時間410ms 食べる時間200ms 考える時間200ms

1619187283000 2 has taken a fork

1619187283000 2 has taken a fork

1619187283000 2 is eating

1619187283001 1 has taken a fork

1619187283200 2 is sleeping

1619187283200 1 has taken a fork

1619187283200 1 is eating

1619187283200 3 has taken a fork

1619187283400 2 is thinking

1619187283400 1 is sleeping

1619187283400 3 has taken a fork

1619187283400 3 is eating

1619187283400 2 has taken a fork

1619187283410 2 died

10

11 of 40

各課題のルール

11

philo_one

philo_two

philo_three

複数の処理を並行して

行う方法

スレッド

スレッド

プロセス

共有するデータへの�アクセスを制御する方法

ミューテックス

セマフォ

セマフォ

フォークの配置

⚠️注意

古い課題に沿った内容です

philo_one が現在の philo 相当�philo_three が現在の philo_bonus 相当です

12 of 40

必要な知識

複数の処理を並行して行う方法(並行/並列)

  • プロセス
  • スレッド

共有するデータへのアクセスを制御する方法(排他処理)

  • ミューテックス
  • セマフォ

12

13 of 40

並行/並列

13

14 of 40

並行と並列

並行:複数タスクを非逐次的に実行

並列:複数タスクを同時に実行

14

15 of 40

なぜ処理を並行/並列させるか

// 逐次処理もできると思うけど、複雑になってしまう

�if (哲学者1 == 食事状態)

{

if (哲学者2 == 食事状態)

{

// 哲学者2の食事処理

}

else if (哲学者2 == 睡眠状態)

{

// 哲学者2の睡眠処理

}

...

}

15

16 of 40

なぜ処理を並行/並列させるか

タスクごとに並行/並列で実行させると、複雑さを軽減できる

並列実行できる環境なら、逐次処理よりも処理速度が向上する

並行/並列で実行する方法として

マルチプロセスマルチスレッドがある

16

17 of 40

プロセスとは

実行中のプログラム。psで一覧を見られる

17

PID TTY TIME CMD

61440 ttys001 0:00.14 -zsh

61584 ttys001 0:00.00 -zsh

61585 ttys001 0:00.00 -zsh

.� .

.

18 of 40

マルチプロセス

OSはプロセスごとにCPUを使う時間を割り当てている

�C言語では、fork() を使ってプロセスを複製し、

マルチプロセスなプログラムが作れる

18

19 of 40

forkの使い方

void fork_process(void)

{

pid_t pid;

pid = fork();

if (pid < 0)

エラー処理

if (pid == 0)

複製したプロセス(子プロセス)で行う処理

else

複製元のプロセス(親プロセス)で行う処理

}

19

20 of 40

forkで起こること

メモリ空間、�ファイルディスクリプタテーブル�などがコピーされる

変数の値がコピーされる

親プロセス(1000)が�子プロセスを(1100)をforkした場合

20

21 of 40

スレッドとは

1つのプロセスで複数の処理を�並行/並列に行うための仕組み

21

22 of 40

スレッドとは

プロセスの違い

  • メモリ空間は共有
  • 固有のスタック、�レジスタの状態を持つ

→ 変数を共有できる

22

23 of 40

スレッドの使い方

スレッドの作成

  • pthread_create

スレッドの終了:2方法

  • pthread_join
  • pthread_detach

23

24 of 40

スレッドの使い方

void *do_something(void *arg)

{

printf("do something\n");

sleep(1);

}

int main(void)

{

pthread_t thread;

void *retval;

// スレッドを作成

if (pthread_create(&thread, NULL, do_something, NULL) != 0)

// エラー処理

return (0);

// スレッドの終了方法

// 1. スレッドが終了するまで待つ

pthread_join(thread, &retval);

// 2. スレッドを待たない

pthread_detach(thread);

}

24

25 of 40

変数を共有するときの問題点

複数同時に共有データへ�アクセスすると不整合が発生

共有データへのアクセスを�制限する必要がある

排他制御

例:3本のフォークを2つのスレッド�  で1本ずつ減らす

25

26 of 40

排他制御

26

27 of 40

排他制御とは

共有するデータに同時に�アクセスして不整合が�起こらないようにする仕組み

今回は

  • ミューテックス
  • セマフォ

を使う

27

28 of 40

ミューテックス

pthreadで用意されている排他制御の仕組み

mutex:mutual exclusion (相互排他) の略

ミューテックスにロックをかけることで、�1つの処理だけ通れるようにする

28

29 of 40

ミューテックスの使い方

初期化

  • pthread_mutex_init

リソース解放

  • pthread_mutex_destroy

ロック(他の処理を制限)

  • pthread_mutex_lock

ロック解除

  • pthread_mutex_unlock

29

30 of 40

セマフォ

排他制御の仕組みの一つ

個数つきミューテックスのような物

ミューテックスと違い、指定個数�だけ同時にロックを獲得できる

30

31 of 40

セマフォの使い方

セマフォの初期化・オープン

  • sem_open

セマフォを削除

  • sem_unlink

セマフォをロック(数を1つ減らす)

  • sem_wait

セマフォのロック解除�(数を1つ増やす)

  • sem_post

31

32 of 40

排他制御の注意点

排他制御をかける順序によっては�デッドロックが発生する

デッドロックとは?� それぞれの処理が� それぞれが取得するロックの解除� を待つ状態

デッドロックが発生しないよう、�排他制御をかける順序を工夫する

デッドロックの例

Benjamin D. Esham / Wikimedia Commons, CC 表示-継承 3.0, https://commons.wikimedia.org/w/index.php?curid=56559による

32

33 of 40

Philosophersの実装

33

34 of 40

philo_one

各フォークをミューテックスで表す

アルゴリズム� 右 → 左の順にフォークを持つ

デッドロック対策� 奇数の番号の哲学者だけ� 200μs待つ

34

usleep

🍝

💤

🍝

💤

🍝

35 of 40

philo_one

死亡監視� 監視用スレッドを立てる

35

36 of 40

philo_two

フォークをセマフォ1つで表す

アルゴリズム� ウェイターの許可を得た哲学者� のみ2本のフォークを取れる

死亡監視� 監視用スレッドを立てる

36

🍝

🍝

💤

37 of 40

philo_three

philo_twoとほぼ同じ

プロセスはメモリ空間でデータを�共有できない

プロセスの戻り値を使って�哲学者の死を通知

死亡したら全てのプロセスをkill

37

38 of 40

参考文献・サイト

38

39 of 40

おしながき

  • Philosophersはどんな課題か
  • 並行/並列
  • 排他処理
  • Philosophersの実装

39

40 of 40

ありがとうございました!

不明点などありましたら、Discordにてお願いします

40