30代専業主婦の独学エンジニア挑戦ブログ

実務未経験の30歳の専業主婦が独学でエンジニアを目指すブログです

「コーディングを支える技術」を読みました(3)

勉強記録 

今日は「コーディングを支える技術」を読みました。

第10章から第12章にかけて、重要そうなところや新たに学んだことを中心にまとめました。

 

コーディングを支える技術 ~成り立ちから学ぶプログラミング作法 (WEB+DB PRESS plus)
 

 

<第10章> 並行処理

並行処理とはなにか

複数の処理を時間軸でオーバーラップして実行することを並行処理という。
並行処理を実現するために、プロセスやスレッドなどの概念が誕生。
並列処理を行うことで新しい問題もでてきたが、それに対処するためにロックやファイバーなどの概念が発明された。
 

細かく区切って実行する

複数の処理を同時に実行するには、人間が気づかないくらいの短い間隔で、複数の処理を切り替えながら実行する。
 

処理を切り替える方法

「どういうときに処理を交代するのか」の決め方は2通りある。
 
協調性マルチタスク
協調性マルチタスクは切りのいいところで交代する方法。
しかし、協調性マルチタスクではどれか1つの処理が「交代していいよ」と言わない限り、他の処理にいつまで経っても交代しないという問題がある。
あくまで「すべての処理が、適度な間隔で交代して良い、と言う信頼のもとで成り立っている」システム。
 
プリエンプティブマルチタスク
プリエンプティブマルチタスクは一定時間で交代する方法。
一定時間ごとに今走っている処理を強制的に中断させて、ほかのプログラムが処理を実行できるようにする。
プリエンプティブは「他人の行動を阻止するための」という意味で、協調性マルチタスクと違って「止められる側のプログラムの協力」がなくても強制的に処理を止めてしまう。
 

競合状態を防ぐには

プリエンプティブマルチタスクでは、いつ「交代しなさい」と言われるかわからない状況で、ちゃんと動くプログラムを書くのは難しいことだった。
並行処理では、競合状態(レースコンディション)が起きることがあり、このプログラムは「スレッドセーブでない」と表現したりもする。
 

競合状態の3条件

1. 2つの処理が変数を共有している
2. 少なくとも1つの処理がその変数を書き換える
3. 一方の処理がひと段落落ち着くまえに、もう一方の処理が割り込む可能性がある
 
この3つの条件のどれか1つでもなくせば、並行処理を実行しても安全なプログラムを書くことができる。
 

共有しない

プロセスではメモリを共有しない
UNIXでは実行中のプログラムを「プロセス」と呼ぶ。異なるプロセスはメモリを共有しない。
データベースへの接続、ファイルの読み書き、など何かを共有したときだけ気をつける。
 
のちに「軽量プロセス」がつくられ、これが「スレッド」と呼ばれるようになった。
今でもスレッドを使って、共有メモリの扱いに悩みながらプログラムが書かれている。
 
アクターモデル
メモリを共有しない設計方針で、もう一つの流れがアクターモデル
ErlangScalaなどのアクターモデルを採用した言語が実装に使われている。
 

書き換えない

メモリを共有しても、書き換えなければ問題ないというアプローチもある。
すべての値が変更不可能であるHaskellなど。
Scalaではvarとvalの2通りの変数宣言があり、valで宣言したものは書き換えられない。
 

割り込まない

協調的なスレッドを使う
ファイバーやコルーチン、グリーンスレッドなどと呼ばれている手法。
協調的なスレッドを作れば良いというアプローチ。
 
割り込まれると困る処理中は印をつける
「今割り込まれると困る」という印を共有する方法。
いろいろなバリエーションがあり、ロック、ミューテックスセマフォなど。
 

ロックの問題点

デッドロックが発生してしまう
デッドロックはお互いにロックが解放するのを待ってしまい、処理が進まない状態のこと。
何をロックすべきかだけではなく、どういう順でロックすべきかも把握しなければならない。
 
 

<第11章> オブジェクトとクラス

言語によってオブジェクト指向の意味が違う

少なくとも2人のオブジェクト指向言語の設計者が、「オブジェクト指向」という言葉を全く違う意味に使っている。
特に型と継承に関しては意見が逆。
 

クラスが持つ3つの役割

1. まとまったものを作る生成器
2. どういう操作が可能かという仕様
3. コードを再利用する単位
 
1は、よく言われる「クラスはたい焼きをつくるためのたい焼きの型」という表現。
2については、Javaではこの役割に特化した「インターフェース」が作られた。
3については、継承がそれにあたるが詳しくは12章で。
 

<第12章> 継承によるコードの再利用

継承は諸刃の剣

クラスを継承して差分を実装するというコーディングスタイルは、深い継承ツリーを作り出し、コードをわかりにくくしてしまう可能性が高い。
 
コードの再利用のために継承を行うのではなく、親子関係のために継承を行う。
実際にGo言語では継承がない。