C言語でのスレッド処理

C言語でのスレッド処理と、ロックの仕方をまとめました。
他の言語のようにスレッド用のクラスを継承するのでは無く、
別スレッドで実行する関数のポインタと、
その関数に渡すデータのポインタを指定して実行するようです。

スレッドによる並行処理

スレッドの作成(pthread_create)

Cではpthread_createを利用することで、別スレッドで任意の関数を実行できます。

int pthread_create(pthread_t * thread,
	 pthread_attr_t * attr,
	 void * (*start_routine)(void *),
	 void * arg);
  • thread
    • スレッド管理用のpthread_t型の変数
  • attr
    • スレッドの属性を指定する。
    • NULLの場合はデフォルトが使われる
  • (*start_routine)(void *)
    • 別スレッドから呼び出される関数へのポインタ
  • arg
    • start_routineの引数として渡すデータのポインタ
    • 元のスレッドからデータを送るのに使う

スレッドの終了を待つ(pthread_join)

pthread_joinで、指定したスレッドが終了するまで待機することができます。

int pthread_join(pthread_t th, void **thread_return);
  • th
    • 待機するスレッドをpthread_t型の変数で指定する
  • **thread_return
    • スレッドの戻り値を格納する領域

サンプルコード

以下の例はグローバルな値にメインとサブの2つのスレッドから加算処理を行っています。
排他制御をしていないため、スレッドによる並行処理が行われると、値がおかしくなる可能性があります。

実際、何度か実行すると値がおかしくなり、並行処理が行われていることが確認できます。

なお、コンパイルする際はは-pthreadオプションを指定する必要があります。

#include "stdio.h"
#include "pthread.h"

int a = 0;

void *func_thread(void *p) {
  printf("start %d\n", *(int*)p);

  int i=0;
  for(i=0; i < 10000; i++){
	int next = a + 1;
	int now = a;
	a = next;
	if (now+1 != next) {
	  printf("other theard change %d %d\n", a+1, next);
	}
  }

  return 0;
}

int main(void) {
  printf("test\n");

  int b = 42;

  pthread_t pthread;
  pthread_create( &pthread, NULL, &func_thread, &b);

  int i=0;
  for(i=0; i < 10000; i++){
	int next = a + 1;
	int now = a;
	a = next;
	if (now+1 != next) {
	  printf("other theard change %d %d\n", a+1, next);
	}
  }

  pthread_join(pthread, NULL); // pthreadで作られたスレッドが終わるまで待つ
  printf("a=%d\n", a);

  return 0;
}

mutexによるロック処理

排他制御を行う

pthread_mutex_t型の変数に対して、pthread_mutex_lockpthread_mutex_unlockを実行することで、
処理をロックすることができます。

pthread_mutex_t型の変数はpthread_mutex_initで初期化することができます。
このとき、第二引数にmutex属性を渡すことができ、NULLを渡した場合はデフォルト値が使われます。

サンプルコード

上記の例にロックによる排他制御を入れました。
そのため、並列処理を行っても値は正しく処理されるため、何度やっても結果が正しくなります。

#include "stdio.h"
#include "pthread.h"

int a = 0;
pthread_mutex_t mutex;

void *func_thread(void *p) {
  printf("start %d\n", *(int*)p);

  int i=0;
  for(i=0; i < 10000; i++){
    pthread_mutex_lock(&mutex);
	int next = a + 1;
	int now = a;
	a = next;
    pthread_mutex_unlock(&mutex);

	if (now+1 != next) {
	  printf("other theard change %d %d\n", a+1, next);
	}
  }

  return 0;
}

int main(void) {
  pthread_mutex_init(&mutex, NULL);
  
  printf("test\n");

  int b = 42;

  pthread_t pthread;
  pthread_create( &pthread, NULL, &func_thread, &b);

  int i=0;
  for(i=0; i < 10000; i++){
    pthread_mutex_lock(&mutex);
	int next = a + 1;
	int now = a;
	a = next;
    pthread_mutex_unlock(&mutex);

	if (now+1 != next) {
	  printf("other theard change %d %d\n", a+1, next);
	}
  }

  pthread_join(pthread, NULL); // pthreadで作られたスレッドが終わるまで待つ
  printf("a=%d\n", a);

  return 0;
}