第5章: ソフトウェアアーキテクチャと実装戦略

5.1 ソフトウェアアーキテクチャの原則

Parnasの情報隠蔽(1972年)

David L. Parnasは1972年に情報隠蔽(Information Hiding)の原則を提唱した("On the Criteria To Be Used in Decomposing Systems into Modules", Communications of the ACM, 1972)。

原則:

モジュールは「変更される可能性のある設計決定」を隠蔽すべきである。
インターフェースは安定した抽象を提供し、実装の詳細を隠す。

Parnasの2つの分解基準:

従来の基準(フローチャートベース):
  1. 処理のステップに分割
  2. 各ステップをモジュールに
  → 変更に弱い

新しい基準(情報隠蔽ベース):
  1. 変更される可能性のある決定を特定
  2. 各決定を1つのモジュールに隠蔽
  → 変更に強い

Philosophersプロジェクトへの適用:

変更される可能性のある決定:
1. 時間管理の方法 → time.c に隠蔽
2. 出力フォーマット → utils.c に隠蔽
3. 同期メカニズム → mutex操作を関数でラップ
4. デッドロック回避戦略 → routine.c に隠蔽

UNIXの設計哲学

Ken Thompson と Dennis Ritchie によるUNIXの設計哲学(1978年):

Doug McIlroyの要約:

1. 1つのことを行い、うまく行う
2. 協調して動作するプログラムを書く
3. テキストストリームを扱う(普遍的インターフェース)

Philosophersの設計への適用:

ファイル構成(単一責任の原則):

main.c     → プログラムのエントリーポイントと全体制御
init.c     → 初期化のみ
routine.c  → 哲学者の行動ロジック
monitor.c  → 監視ロジック
time.c     → 時間管理のみ
utils.c    → ユーティリティ関数
cleanup.c  → リソース解放のみ

結合度と凝集度

Larry Constantine と Edward Yourdon は1979年に結合度(Coupling)凝集度(Cohesion)を定式化した("Structured Design", 1979):

結合度(低い方が良い):

内容結合(最悪): 一方が他方の内部を直接操作
共通結合: グローバル変数を共有
制御結合: 制御フラグを渡す
スタンプ結合: 構造体を渡す
データ結合(最良): 単純なデータのみ渡す

凝集度(高い方が良い):

偶発的凝集(最悪): 関連のない機能の集まり
論理的凝集: 似た機能(全て「初期化」など)
時間的凝集: 同時に実行される機能
手続き的凝集: 順序に従う機能
通信的凝集: 同じデータを扱う機能
逐次的凝集: 出力が次の入力になる
機能的凝集(最良): 単一の明確な目的

5.2 データ構造設計の理論

キャッシュ局所性(Cache Locality)

Hennessy と Patterson は "Computer Architecture: A Quantitative Approach"(1990年)において、キャッシュ効率の重要性を解説した:

時間的局所性(Temporal Locality):

最近アクセスしたデータは再度アクセスされる可能性が高い
→ 頻繁にアクセスするデータをまとめる

空間的局所性(Spatial Locality):

あるアドレスへのアクセスは、近くのアドレスへのアクセスを伴う
→ 連続したメモリ配置が効率的

キャッシュラインの考慮:

典型的なキャッシュラインサイズ: 64バイト

悪い例: 構造体メンバがキャッシュラインをまたぐ
良い例: 関連データを64バイト境界に収める

構造体のメモリレイアウト

パディングとアラインメント:

/* 悪い例: パディングが多い */
struct bad_layout {
    char a;       /* 1 byte + 7 padding */
    long b;       /* 8 bytes */
    char c;       /* 1 byte + 7 padding */
    long d;       /* 8 bytes */
};  /* 合計: 32 bytes(実データ: 18 bytes) */

/* 良い例: メンバを大きさ順に並べる */
struct good_layout {
    long b;       /* 8 bytes */
    long d;       /* 8 bytes */
    char a;       /* 1 byte */
    char c;       /* 1 byte + 6 padding */
};  /* 合計: 24 bytes(実データ: 18 bytes) */

False Sharing の回避

異なるスレッドが同じキャッシュラインのデータを更新すると、パフォーマンスが大幅に低下する:

/* 悪い例: false sharing が発生 */
struct shared_data {
    int counter_thread_1;  /* 同じキャッシュラインに */
    int counter_thread_2;  /* 配置される可能性 */
};

/* 良い例: キャッシュライン境界で分離 */
struct shared_data {
    int counter_thread_1;
    char padding[60];      /* 64バイト境界まで埋める */
    int counter_thread_2;
};

5.3 時間管理の理論

POSIXの時間概念

POSIXは複数の時計(clock)を定義している:

CLOCK_REALTIME:

実世界の時刻(wall clock time)
システム管理者やNTPにより変更される可能性
1970年1月1日 00:00:00 UTC からの経過時間

CLOCK_MONOTONIC:

単調増加する時計
システム起動からの経過時間
時刻の巻き戻りなし(経過時間測定に適切)

gettimeofday() の仕様:

#include <sys/time.h>

int gettimeofday(struct timeval *tv, struct timezone *tz);

struct timeval {
    time_t      tv_sec;   /* 秒 */
    suseconds_t tv_usec;  /* マイクロ秒 (0-999999) */
};

リアルタイムシステムの時間精度

並行システムにおける時間精度は重要である。Philosophersでは、ミリ秒単位の精度が要求される:

usleep() の非精度性:

usleep(n) は「少なくとも n マイクロ秒」スリープする
スケジューリング遅延により、実際の遅延は長くなる可能性
典型的な誤差: 数ミリ秒~数十ミリ秒

精密なスリープの実装戦略:

/* 戦略1: ビジーウェイト(CPU負荷高) */
void busy_wait_ms(long ms)
{
    long start = get_time_ms();
    while (get_time_ms() - start < ms)
        ;  /* CPU 100% 使用 */
}

/* 戦略2: ハイブリッド(推奨) */
void precise_sleep_ms(long ms)
{
    long start = get_time_ms();
    long remaining;

    while (1)
    {
        remaining = ms - (get_time_ms() - start);
        if (remaining <= 0)
            break;

        if (remaining > 10)
            usleep(5000);   /* 大きな遅延: 5ms スリープ */
        else if (remaining > 1)
            usleep(500);    /* 中程度: 0.5ms スリープ */
        else
            usleep(100);    /* 微調整: 0.1ms スリープ */
    }
}

5.4 防御的プログラミング

McConnellの防御的プログラミング原則

Steve McConnell は "Code Complete"(1993年, 2004年改訂)において、防御的プログラミングの原則を体系化した:

原則1: 不正な入力から身を守る:

int init_data(t_data *data, int argc, char **argv)
{
    /* 引数のNULLチェック */
    if (!data || !argv)
        return (error_return("NULL pointer"));

    /* 引数の範囲チェック */
    if (argc < 5 || argc > 6)
        return (error_return("Invalid argument count"));

    /* 値の妥当性チェック */
    data->num_philos = ft_atoi(argv[1]);
    if (data->num_philos <= 0 || data->num_philos > 200)
        return (error_return("Invalid philosopher count"));

    /* ... */
}

原則2: アサーションを使う:

#include <assert.h>

void philosopher_routine(t_philo *philo)
{
    /* 開発時のみ有効な検証 */
    assert(philo != NULL);
    assert(philo->data != NULL);
    assert(philo->id >= 1 && philo->id <= philo->data->num_philos);

    /* ... */
}

原則3: エラーを早期に発見する:

int init_mutexes(t_data *data)
{
    int i;
    int ret;

    data->forks = malloc(sizeof(pthread_mutex_t) * data->num_philos);
    if (!data->forks)
        return (-1);  /* 早期リターン */

    i = 0;
    while (i < data->num_philos)
    {
        ret = pthread_mutex_init(&data->forks[i], NULL);
        if (ret != 0)
        {
            /* エラー発生時、即座にクリーンアップ */
            while (--i >= 0)
                pthread_mutex_destroy(&data->forks[i]);
            free(data->forks);
            data->forks = NULL;
            return (-1);
        }
        i++;
    }
    return (0);
}

エラー処理の設計パターン

パターン1: エラーコードの伝播:

int create_philosophers(t_data *data)
{
    int ret;

    ret = init_data(data);
    if (ret != 0)
        return (ret);  /* エラーを伝播 */

    ret = init_mutexes(data);
    if (ret != 0)
    {
        cleanup_data(data);
        return (ret);
    }

    ret = init_philos(data);
    if (ret != 0)
    {
        cleanup_mutexes(data);
        cleanup_data(data);
        return (ret);
    }

    return (0);
}

パターン2: リソース取得初期化(RAII風):

/* 初期化と解放をペアで管理 */
typedef struct s_resources {
    int data_initialized;
    int mutexes_initialized;
    int philos_initialized;
    int threads_created;
} t_resources;

void cleanup_by_state(t_data *data, t_resources *res)
{
    if (res->threads_created)
        join_all_threads(data);
    if (res->philos_initialized)
        free(data->philos);
    if (res->mutexes_initialized)
        destroy_mutexes(data);
    if (res->data_initialized)
        cleanup_data(data);
}

5.5 プロジェクト構造の設計

ヘッダーファイルの設計

/* philo.h */

#ifndef PHILO_H
# define PHILO_H

/*
** 標準ライブラリ
*/
# include <stdio.h>
# include <stdlib.h>
# include <unistd.h>
# include <pthread.h>
# include <sys/time.h>

/*
** 定数定義
*/
# define SUCCESS 0
# define FAILURE -1
# define TRUE 1
# define FALSE 0

/*
** 型定義(前方宣言)
*/
typedef struct s_data   t_data;
typedef struct s_philo  t_philo;

/*
** 哲学者構造体
** - 各哲学者の状態を管理
** - スレッドから独立してアクセスされる
*/
struct s_philo
{
    int         id;              /* 識別番号 (1-N) */
    int         left_fork_id;    /* 左フォークインデックス */
    int         right_fork_id;   /* 右フォークインデックス */
    int         meals_eaten;     /* 食事回数 */
    long        last_meal_time;  /* 最終食事時刻 (ms) */
    pthread_t   thread;          /* スレッドハンドル */
    t_data      *data;           /* グローバルデータへの参照 */
};

/*
** グローバルデータ構造体
** - 全スレッドで共有される情報
** - ミューテックスで保護が必要なフィールドあり
*/
struct s_data
{
    /* 設定値(読み取り専用) */
    int             num_philos;
    long            time_to_die;
    long            time_to_eat;
    long            time_to_sleep;
    int             must_eat_count;

    /* 実行時状態(ミューテックス保護要) */
    long            start_time;
    int             simulation_end;

    /* 同期プリミティブ */
    pthread_mutex_t *forks;
    pthread_mutex_t print_mutex;
    pthread_mutex_t state_mutex;
    pthread_mutex_t meal_mutex;

    /* 哲学者配列 */
    t_philo         *philos;
};

/*
** 関数プロトタイプ: init.c
*/
int     init_all(t_data *data, int argc, char **argv);
int     parse_arguments(t_data *data, int argc, char **argv);
int     init_mutexes(t_data *data);
int     init_philosophers(t_data *data);

/*
** 関数プロトタイプ: routine.c
*/
void    *philosopher_routine(void *arg);
int     take_forks(t_philo *philo);
void    release_forks(t_philo *philo);

/*
** 関数プロトタイプ: actions.c
*/
void    philo_eat(t_philo *philo);
void    philo_sleep(t_philo *philo);
void    philo_think(t_philo *philo);

/*
** 関数プロトタイプ: monitor.c
*/
void    *monitor_routine(void *arg);
int     check_death(t_data *data);
void    set_simulation_end(t_data *data);

/*
** 関数プロトタイプ: time.c
*/
long    get_timestamp_ms(void);
void    precise_sleep(long duration_ms);

/*
** 関数プロトタイプ: utils.c
*/
int     ft_atoi_safe(const char *str, int *result);
void    print_status(t_philo *philo, const char *status);
void    print_death(t_philo *philo);
int     error_exit(const char *msg);

/*
** 関数プロトタイプ: cleanup.c
*/
void    cleanup_all(t_data *data);
void    destroy_mutexes(t_data *data);
void    join_threads(t_data *data);

#endif

ソースファイルの構成

philo/
├── Makefile
├── includes/
│   └── philo.h
└── srcs/
    ├── main.c          # エントリーポイント
    ├── init.c          # 初期化関数
    ├── routine.c       # 哲学者スレッドルーチン
    ├── actions.c       # 個別アクション(eat, sleep, think)
    ├── monitor.c       # 監視スレッド
    ├── time.c          # 時間関連ユーティリティ
    ├── utils.c         # 汎用ユーティリティ
    └── cleanup.c       # リソース解放

5.6 初期化の実装

メイン関数

/* main.c */

#include "philo.h"

int main(int argc, char **argv)
{
    t_data  data;

    /* 構造体をゼロ初期化 */
    memset(&data, 0, sizeof(t_data));

    /* 全体初期化 */
    if (init_all(&data, argc, argv) != SUCCESS)
        return (EXIT_FAILURE);

    /* スレッド作成・実行 */
    if (start_simulation(&data) != SUCCESS)
    {
        cleanup_all(&data);
        return (EXIT_FAILURE);
    }

    /* 監視(メインスレッドで実行) */
    monitor_simulation(&data);

    /* クリーンアップ */
    cleanup_all(&data);

    return (EXIT_SUCCESS);
}

引数パース

/* init.c */

/*
** 安全な文字列→整数変換
** オーバーフローと不正な文字をチェック
*/
int ft_atoi_safe(const char *str, int *result)
{
    long    value;
    int     i;
    int     sign;

    value = 0;
    sign = 1;
    i = 0;

    /* 空白スキップ */
    while (str[i] == ' ' || (str[i] >= 9 && str[i] <= 13))
        i++;

    /* 符号処理 */
    if (str[i] == '-' || str[i] == '+')
    {
        if (str[i] == '-')
            sign = -1;
        i++;
    }

    /* 数字以外の文字チェック */
    if (str[i] == '\0')
        return (FAILURE);

    /* 数値変換(オーバーフローチェック付き) */
    while (str[i])
    {
        if (str[i] < '0' || str[i] > '9')
            return (FAILURE);
        value = value * 10 + (str[i] - '0');
        if (value > INT_MAX)
            return (FAILURE);
        i++;
    }

    *result = (int)(value * sign);
    return (SUCCESS);
}

int parse_arguments(t_data *data, int argc, char **argv)
{
    int value;

    if (argc < 5 || argc > 6)
        return (error_exit("Usage: ./philo num time_die time_eat time_sleep [must_eat]"));

    if (ft_atoi_safe(argv[1], &value) != SUCCESS || value <= 0)
        return (error_exit("Invalid number of philosophers"));
    data->num_philos = value;

    if (ft_atoi_safe(argv[2], &value) != SUCCESS || value <= 0)
        return (error_exit("Invalid time_to_die"));
    data->time_to_die = value;

    if (ft_atoi_safe(argv[3], &value) != SUCCESS || value <= 0)
        return (error_exit("Invalid time_to_eat"));
    data->time_to_eat = value;

    if (ft_atoi_safe(argv[4], &value) != SUCCESS || value <= 0)
        return (error_exit("Invalid time_to_sleep"));
    data->time_to_sleep = value;

    if (argc == 6)
    {
        if (ft_atoi_safe(argv[5], &value) != SUCCESS || value <= 0)
            return (error_exit("Invalid must_eat_count"));
        data->must_eat_count = value;
    }
    else
        data->must_eat_count = -1;

    return (SUCCESS);
}

ミューテックス初期化

int init_mutexes(t_data *data)
{
    int i;

    /* フォーク配列のメモリ確保 */
    data->forks = malloc(sizeof(pthread_mutex_t) * data->num_philos);
    if (!data->forks)
        return (FAILURE);

    /* 各フォークのミューテックス初期化 */
    i = 0;
    while (i < data->num_philos)
    {
        if (pthread_mutex_init(&data->forks[i], NULL) != 0)
        {
            /* エラー: 既に初期化したものを破棄 */
            while (--i >= 0)
                pthread_mutex_destroy(&data->forks[i]);
            free(data->forks);
            data->forks = NULL;
            return (FAILURE);
        }
        i++;
    }

    /* 出力保護用ミューテックス */
    if (pthread_mutex_init(&data->print_mutex, NULL) != 0)
    {
        destroy_fork_mutexes(data);
        return (FAILURE);
    }

    /* 状態保護用ミューテックス */
    if (pthread_mutex_init(&data->state_mutex, NULL) != 0)
    {
        pthread_mutex_destroy(&data->print_mutex);
        destroy_fork_mutexes(data);
        return (FAILURE);
    }

    /* 食事時刻保護用ミューテックス */
    if (pthread_mutex_init(&data->meal_mutex, NULL) != 0)
    {
        pthread_mutex_destroy(&data->state_mutex);
        pthread_mutex_destroy(&data->print_mutex);
        destroy_fork_mutexes(data);
        return (FAILURE);
    }

    return (SUCCESS);
}

哲学者初期化

int init_philosophers(t_data *data)
{
    int i;

    data->philos = malloc(sizeof(t_philo) * data->num_philos);
    if (!data->philos)
        return (FAILURE);

    i = 0;
    while (i < data->num_philos)
    {
        data->philos[i].id = i + 1;
        data->philos[i].left_fork_id = i;
        data->philos[i].right_fork_id = (i + 1) % data->num_philos;
        data->philos[i].meals_eaten = 0;
        data->philos[i].last_meal_time = data->start_time;
        data->philos[i].data = data;
        i++;
    }

    return (SUCCESS);
}

5.7 哲学者ルーチンの実装

メインループ

/* routine.c */

void *philosopher_routine(void *arg)
{
    t_philo *philo;

    philo = (t_philo *)arg;

    /* 偶数IDは開始を遅延(デッドロック回避) */
    if (philo->id % 2 == 0)
        precise_sleep(philo->data->time_to_eat / 2);

    while (!check_simulation_end(philo->data))
    {
        /* 思考 */
        philo_think(philo);

        /* フォーク取得 */
        if (!take_forks(philo))
            break;

        /* 食事 */
        philo_eat(philo);

        /* フォーク返却 */
        release_forks(philo);

        /* 必要回数達成チェック */
        if (check_meals_complete(philo))
            break;

        /* 睡眠 */
        philo_sleep(philo);
    }

    return (NULL);
}

フォーク取得(デッドロック回避)

int take_forks(t_philo *philo)
{
    int first, second;

    /* リソース順序付けによるデッドロック回避 */
    if (philo->left_fork_id < philo->right_fork_id)
    {
        first = philo->left_fork_id;
        second = philo->right_fork_id;
    }
    else
    {
        first = philo->right_fork_id;
        second = philo->left_fork_id;
    }

    /* 1本目のフォーク取得 */
    pthread_mutex_lock(&philo->data->forks[first]);
    if (check_simulation_end(philo->data))
    {
        pthread_mutex_unlock(&philo->data->forks[first]);
        return (FALSE);
    }
    print_status(philo, "has taken a fork");

    /* 1人の場合の特殊処理 */
    if (philo->data->num_philos == 1)
    {
        precise_sleep(philo->data->time_to_die + 10);
        pthread_mutex_unlock(&philo->data->forks[first]);
        return (FALSE);
    }

    /* 2本目のフォーク取得 */
    pthread_mutex_lock(&philo->data->forks[second]);
    if (check_simulation_end(philo->data))
    {
        pthread_mutex_unlock(&philo->data->forks[second]);
        pthread_mutex_unlock(&philo->data->forks[first]);
        return (FALSE);
    }
    print_status(philo, "has taken a fork");

    return (TRUE);
}

void release_forks(t_philo *philo)
{
    pthread_mutex_unlock(&philo->data->forks[philo->left_fork_id]);
    pthread_mutex_unlock(&philo->data->forks[philo->right_fork_id]);
}

行動の実装

/* actions.c */

void philo_think(t_philo *philo)
{
    print_status(philo, "is thinking");
}

void philo_eat(t_philo *philo)
{
    print_status(philo, "is eating");

    /* 食事時刻を更新(ミューテックス保護) */
    pthread_mutex_lock(&philo->data->meal_mutex);
    philo->last_meal_time = get_timestamp_ms();
    philo->meals_eaten++;
    pthread_mutex_unlock(&philo->data->meal_mutex);

    /* 食事時間待機 */
    precise_sleep(philo->data->time_to_eat);
}

void philo_sleep(t_philo *philo)
{
    print_status(philo, "is sleeping");
    precise_sleep(philo->data->time_to_sleep);
}

5.8 監視スレッドの実装

死亡監視ループ

/* monitor.c */

void *monitor_routine(void *arg)
{
    t_data  *data;
    int     i;

    data = (t_data *)arg;

    while (!check_simulation_end(data))
    {
        i = 0;
        while (i < data->num_philos)
        {
            if (is_philosopher_dead(&data->philos[i]))
            {
                print_death(&data->philos[i]);
                set_simulation_end(data);
                return (NULL);
            }
            i++;
        }

        /* 全員が必要回数食べたかチェック */
        if (all_philosophers_finished(data))
        {
            set_simulation_end(data);
            return (NULL);
        }

        usleep(1000);  /* 1ms間隔でチェック */
    }

    return (NULL);
}

死亡判定

int is_philosopher_dead(t_philo *philo)
{
    long    current_time;
    long    last_meal;
    long    elapsed;

    current_time = get_timestamp_ms();

    pthread_mutex_lock(&philo->data->meal_mutex);
    last_meal = philo->last_meal_time;
    pthread_mutex_unlock(&philo->data->meal_mutex);

    elapsed = current_time - last_meal;

    return (elapsed > philo->data->time_to_die);
}

シミュレーション状態管理

int check_simulation_end(t_data *data)
{
    int result;

    pthread_mutex_lock(&data->state_mutex);
    result = data->simulation_end;
    pthread_mutex_unlock(&data->state_mutex);

    return (result);
}

void set_simulation_end(t_data *data)
{
    pthread_mutex_lock(&data->state_mutex);
    data->simulation_end = TRUE;
    pthread_mutex_unlock(&data->state_mutex);
}

5.9 時間管理の実装

タイムスタンプ取得

/* time.c */

long get_timestamp_ms(void)
{
    struct timeval tv;

    gettimeofday(&tv, NULL);
    return ((tv.tv_sec * 1000L) + (tv.tv_usec / 1000L));
}

精密スリープ

void precise_sleep(long duration_ms)
{
    long    start;
    long    elapsed;
    long    remaining;

    start = get_timestamp_ms();

    while (TRUE)
    {
        elapsed = get_timestamp_ms() - start;
        remaining = duration_ms - elapsed;

        if (remaining <= 0)
            break;

        /* 適応的スリープ: 残り時間に応じて調整 */
        if (remaining > 10)
            usleep(5000);       /* 残り10ms以上: 5ms sleep */
        else if (remaining > 2)
            usleep(500);        /* 残り2-10ms: 0.5ms sleep */
        else
            usleep(100);        /* 残り2ms未満: 0.1ms sleep */
    }
}

5.10 出力とユーティリティ

スレッドセーフな出力

/* utils.c */

void print_status(t_philo *philo, const char *status)
{
    long timestamp;

    pthread_mutex_lock(&philo->data->print_mutex);

    if (!check_simulation_end(philo->data))
    {
        timestamp = get_timestamp_ms() - philo->data->start_time;
        printf("%ld %d %s\n", timestamp, philo->id, status);
    }

    pthread_mutex_unlock(&philo->data->print_mutex);
}

void print_death(t_philo *philo)
{
    long timestamp;

    pthread_mutex_lock(&philo->data->print_mutex);

    timestamp = get_timestamp_ms() - philo->data->start_time;
    printf("%ld %d died\n", timestamp, philo->id);

    pthread_mutex_unlock(&philo->data->print_mutex);
}

エラー処理

int error_exit(const char *msg)
{
    write(STDERR_FILENO, "Error: ", 7);
    write(STDERR_FILENO, msg, strlen(msg));
    write(STDERR_FILENO, "\n", 1);
    return (FAILURE);
}

5.11 クリーンアップ

リソース解放

/* cleanup.c */

void cleanup_all(t_data *data)
{
    /* スレッドの終了を待機 */
    join_threads(data);

    /* ミューテックスを破棄 */
    destroy_mutexes(data);

    /* メモリを解放 */
    if (data->philos)
    {
        free(data->philos);
        data->philos = NULL;
    }
    if (data->forks)
    {
        free(data->forks);
        data->forks = NULL;
    }
}

void destroy_mutexes(t_data *data)
{
    int i;

    if (data->forks)
    {
        i = 0;
        while (i < data->num_philos)
        {
            pthread_mutex_destroy(&data->forks[i]);
            i++;
        }
    }

    pthread_mutex_destroy(&data->print_mutex);
    pthread_mutex_destroy(&data->state_mutex);
    pthread_mutex_destroy(&data->meal_mutex);
}

void join_threads(t_data *data)
{
    int i;

    if (!data->philos)
        return;

    i = 0;
    while (i < data->num_philos)
    {
        pthread_join(data->philos[i].thread, NULL);
        i++;
    }
}

5.12 まとめ

本章では、ソフトウェアアーキテクチャと実装戦略について学んだ:

  • アーキテクチャ原則: Parnasの情報隠蔽、UNIXの設計哲学
  • 結合度と凝集度: モジュール設計の品質指標
  • データ構造設計: キャッシュ局所性、メモリアラインメント
  • 時間管理: POSIX時間関数、精密スリープ実装
  • 防御的プログラミング: 入力検証、エラー処理
  • 実装パターン: 初期化、ルーチン、監視、クリーンアップ
  • 次章では、デバッグとテスト手法について学ぶ。

    ---

    参考文献

  • Parnas, D. L. (1972). "On the Criteria To Be Used in Decomposing Systems into Modules", Communications of the ACM, 15(12)
  • Constantine, L. L. & Yourdon, E. (1979). "Structured Design", Prentice-Hall
  • McConnell, S. (2004). "Code Complete", 2nd Edition, Microsoft Press
  • Hennessy, J. L. & Patterson, D. A. (1990). "Computer Architecture: A Quantitative Approach", Morgan Kaufmann
  • McIlroy, M. D. (1978). "UNIX Time-Sharing System: Foreword", The Bell System Technical Journal
  • IEEE (1993). "POSIX.1b-1993: Realtime Extension", IEEE Std 1003.1b-1993
  • Drepper, U. (2007). "What Every Programmer Should Know About Memory", Red Hat Inc.