C言語

【C言語】第9章第6回:並列処理とスレッドの基礎

並列処理は、プログラムの効率を大幅に向上させる手法です。本記事では、スレッドの基本とC言語での実装方法について学びます。

0. 記事の概要

この記事を読むメリット

  • 並列処理の基礎理解:スレッドの動作と利点を学べます。
  • マルチスレッドプログラミング:C言語を使ったスレッドの基本操作を習得します。
  • 実践的なスキル:複数タスクを同時に実行するプログラムを設計できます。

この記事で学べること

  • スレッドの基本概念
  • POSIXスレッド(pthread)の使用方法
  • スレッド同期の基本

1. 並列処理とスレッドの基本

1.1 並列処理とは?

並列処理とは、複数のタスクを同時に実行する手法のことです。これにより、プログラムのパフォーマンスを向上させることができます。

1.2 スレッドとは?

スレッドは、プロセス内で動作する軽量な実行単位です。一つのプロセスは複数のスレッドを持つことができます。

1.3 スレッドの利点と注意点

  • 利点:並列処理による効率化
  • 注意点:競合状態やデッドロックのリスク

2. POSIXスレッド(pthread)の使用

2.1 スレッドの作成と終了

#include <stdio.h>
#include <pthread.h>

// スレッドが実行する関数
void* threadFunction(void* arg) {
    printf("Thread %d is running\n", *(int*)arg);
    return NULL;
}

int main() {
    pthread_t thread1, thread2;
    int id1 = 1, id2 = 2;

    // スレッドの作成
    pthread_create(&thread1, NULL, threadFunction, &id1);
    pthread_create(&thread2, NULL, threadFunction, &id2);

    // スレッドの終了を待機
    pthread_join(thread1, NULL);
    pthread_join(thread2, NULL);

    return 0;
}
動作解説
  1. スレッドの作成:`pthread_create`を使用して新しいスレッドを作成します。
  2. スレッドの終了待機:`pthread_join`を使用してスレッドの終了を待機します。

2.2 スレッド間のデータ共有

#include <stdio.h>
#include <pthread.h>

int sharedValue = 0; // 共有データ

void* increment(void* arg) {
    for (int i = 0; i < 1000; i++) {
        sharedValue++;
    }
    return NULL;
}

int main() {
    pthread_t thread1, thread2;

    // スレッドの作成
    pthread_create(&thread1, NULL, increment, NULL);
    pthread_create(&thread2, NULL, increment, NULL);

    // スレッドの終了を待機
    pthread_join(thread1, NULL);
    pthread_join(thread2, NULL);

    printf("Final shared value: %d\n", sharedValue);
    return 0;
}
動作解説
  1. 共有データの使用:複数のスレッドが同じ変数を操作します。
  2. 競合状態の発生:正しい結果が得られない場合があります。

3. スレッド同期

3.1 ミューテックスを使用した同期

#include <stdio.h>
#include <pthread.h>

int sharedValue = 0; // 共有データ
pthread_mutex_t lock; // ミューテックス

void* increment(void* arg) {
    for (int i = 0; i < 1000; i++) {
        pthread_mutex_lock(&lock); // ロック
        sharedValue++;
        pthread_mutex_unlock(&lock); // ロック解除
    }
    return NULL;
}

int main() {
    pthread_t thread1, thread2;

    // ミューテックスの初期化
    pthread_mutex_init(&lock, NULL);

    // スレッドの作成
    pthread_create(&thread1, NULL, increment, NULL);
    pthread_create(&thread2, NULL, increment, NULL);

    // スレッドの終了を待機
    pthread_join(thread1, NULL);
    pthread_join(thread2, NULL);

    printf("Final shared value: %d\n", sharedValue);

    // ミューテックスの破棄
    pthread_mutex_destroy(&lock);

    return 0;
}
動作解説
  1. ミューテックスの初期化:`pthread_mutex_init`を使用してロックを初期化します。
  2. ロックと解除:共有データを操作する際に`pthread_mutex_lock`と`pthread_mutex_unlock`を使用します。
  3. ロックの破棄:プログラム終了時に`pthread_mutex_destroy`を使用してリソースを解放します。

4. 練習問題

以下の課題に挑戦して、スレッドプログラミングの理解を深めましょう。

  1. 3つのスレッドを作成し、それぞれ異なるメッセージを表示するプログラムを作成してください。
  2. 共有データを操作する際の競合状態を再現するプログラムを作成してください。
  3. ミューテックスを使用して競合状態を防ぐプログラムを実装してください。

5. 練習問題の解答と解説

問3の解答

#include <stdio.h>
#include <pthread.h>

int sharedValue = 0; // 共有データ
pthread_mutex_t lock; // ミューテックス

void* increment(void* arg) {
    for (int i = 0; i < 1000; i++) {
        pthread_mutex_lock(&lock); // ロック
        sharedValue++;
        pthread_mutex_unlock(&lock); // ロック解除
    }
    return NULL;
}

int main() {
    pthread_t thread1, thread2;

    // ミューテックスの初期化
    pthread_mutex_init(&lock, NULL);

    // スレッドの作成
    pthread_create(&thread1, NULL, increment, NULL);
    pthread_create(&thread2, NULL, increment, NULL);

    // スレッドの終了を待機
    pthread_join(thread1, NULL);
    pthread_join(thread2, NULL);

    printf("Final shared value: %d\n", sharedValue);

    // ミューテックスの破棄
    pthread_mutex_destroy(&lock);

    return 0;
}

このプログラムでは、ミューテックスを使用して競合状態を防ぎ、正確な結果を得られるようにしています。

6. まとめ

本記事では、スレッドと並列処理の基本、C言語での実装方法、同期手法について学びました。次回は、さらに高度な並列処理の手法について掘り下げます。