C 言語でアトミックタイプを使用する

胡金庫 2023年10月12日
C 言語でアトミックタイプを使用する

この記事では、C 言語でアトミック型を使用する方法に関する複数の方法を示します。

アトミックタイプを使用して、共有リソースへのアクセスを暗黙的に同期する

アトミックタイプのオブジェクトは、競合状態が発生することなく、複数のスレッドから同時にアクセスおよび変更できる唯一のオブジェクトです。この機能は、さまざまなスレッドからアクセスされるグローバル変数と静的変数に不可欠であり、プログラムの正確性が維持されます。一般に、アトミックタイプのオブジェクトを使用することは、ミューテックスなどのオブジェクトと、mtx_lockmtx_unlock などの標準 API 関数をロックする代わりになります。次のコードサンプルは、複数のスレッドが共有グローバルカウンター変数をインクリメントするカウント問題の単純なケースを示しています。最後に、プログラムの最後に合計が stdout に出力されます。counter を通常の int タイプとして宣言したことに注意してください。残念ながら、一部の実行で正しい結果が得られる場合でも、このプログラムには欠陥があります。

#include <stdio.h>
#include <stdlib.h>
#include <threads.h>
#include <unistd.h>

#ifndef NUM_THREADS
#define NUM_THREADS 4
#endif

int counter = 0;

enum { MAX_ITER = 10000 };

void incrementCounter(void *thr_id) {
  long tid;
  tid = (long)thr_id;
  printf("thread %ld started incrementing ID - %lu\n", tid, thrd_current());

  for (int i = 0; i < MAX_ITER; ++i) {
    counter += 1;
  }

  thrd_exit(EXIT_SUCCESS);
}

int main(int argc, char const *argv[]) {
  thrd_t threads[NUM_THREADS];
  int rc;
  long t;

  for (t = 0; t < NUM_THREADS; t++) {
    rc = thrd_create(&threads[t], (thrd_start_t)incrementCounter, (void *)t);
    if (rc == thrd_error) {
      printf("ERORR; thrd_create() call failed\n");
      exit(EXIT_FAILURE);
    }
  }

  for (t = 0; t < NUM_THREADS; t++) {
    thrd_join(threads[t], NULL);
  }
  printf("count = %d\n", counter);

  thrd_exit(EXIT_SUCCESS);
}

出力:

thread 0 started incrementing ID - 140097636923136
thread 2 started incrementing ID - 140097620137728
thread 1 started incrementing ID - 140097628530432
thread 3 started incrementing ID - 140097611745024
count = 18851

thrd_create 呼び出しで他を作成するメインスレッドはカウンター変数をインクリメントしないため、合計は MAX_ITER 定数とスレッド数を示す NUM_THREADS の倍数である必要があることに注意してください。counter += 1 行をミューテックスのロック/ロック解除関数またはセマフォアクションで囲むことでこの問題を解決できますが、この場合、counteratomic_int のタイプとして宣言するだけです。この整数オブジェクトは現在、アトミックプロパティを持っていると言われています。これは、オブジェクトへのアクセスが中断することなく、単一の命令として行われ、プログラムの順次実行が保証されることを意味します。

#include <stdatomic.h>
#include <stdio.h>
#include <stdlib.h>
#include <threads.h>
#include <unistd.h>

#ifndef NUM_THREADS
#define NUM_THREADS 4
#endif

atomic_int counter = 0;

enum { MAX_ITER = 10000 };

void incrementCounter(void *thr_id) {
  long tid;
  tid = (long)thr_id;
  printf("thread %ld started incrementing ID - %lu\n", tid, thrd_current());

  for (int i = 0; i < MAX_ITER; ++i) {
    counter += 1;
  }

  thrd_exit(EXIT_SUCCESS);
}

int main(int argc, char const *argv[]) {
  thrd_t threads[NUM_THREADS];
  int rc;
  long t;

  for (t = 0; t < NUM_THREADS; t++) {
    rc = thrd_create(&threads[t], (thrd_start_t)incrementCounter, (void *)t);
    if (rc == thrd_error) {
      printf("ERORR; thrd_create() call failed\n");
      exit(EXIT_FAILURE);
    }
  }

  for (t = 0; t < NUM_THREADS; t++) {
    thrd_join(threads[t], NULL);
  }
  printf("count = %d\n", counter);

  thrd_exit(EXIT_SUCCESS);
}

出力:

thread 0 started incrementing ID - 140125987915520
thread 1 started incrementing ID - 140125979522816
thread 2 started incrementing ID - 140125971130112
thread 3 started incrementing ID - 140125962737408
count = 40000
著者: 胡金庫
胡金庫 avatar 胡金庫 avatar

DelftStack.comの創設者です。Jinku はロボティクスと自動車産業で8年以上働いています。自動テスト、リモートサーバーからのデータ収集、耐久テストからのレポート作成が必要となったとき、彼はコーディングスキルを磨きました。彼は電気/電子工学のバックグラウンドを持っていますが、組み込みエレクトロニクス、組み込みプログラミング、フロントエンド/バックエンドプログラミングへの関心を広げています。

LinkedIn Facebook