🐲

SpresenseでNuttXのpthreadsを使用しマルチスレッドを学ぶ #5 〜複数スレッドの待ち状態を一度に全て解除する〜

2022/03/26に公開

これは何?

前回に引き続きSpresenseでpthreadsインターフェースを使って動作確認します。

今回のテーマ

シリーズ#3, #4は条件変数を使ってプログラムの実行を待ち合わせしました。シリーズ#3, #4は条件解除、条件待ちするスレッドが1対1の関係でした。今回は条件解除するスレッドが1つ、複数スレッドが条件待ちする1対Nの構成を確認します。

今回使うpthreadsの関数は次のとおりです。

  • pthread_cond_init:条件変数の初期化
  • pthread_cond_wait:条件待ち
  • pthread_cond_broadcast:全ての条件待ちを解除 ★今回初登場★
  • pthread_cond_destroy:条件変数の削除

確認環境

シリーズ #1と同じです。

テストコード説明

今回のテストコードはGitHub[1]におきました。
ディレクトリ・実行ファイル名称はpthread_cond_broadcastです。

条件変数で待つスレッドは3つで関数名はthread_cond_wait_5です。
条件待ち解除するスレッドは1つで関数名はthread_cond_broadcastです。
APS学習ボードのスイッチを押下するとthread_cond_broadcastで条件待ち解除のpthread_cond_broadcastを呼び出します。
thread_cond_wait_5は条件待ちしており、条件解除されたらスレッドを終了します。

条件変数の定義

条件変数を定義します。

pthread_cond_broadcast_main.c // 該当部分を抽出し記載
pthread_cond_t switch_cond_5;

条件変数の初期化

条件変数を利用するにはpthread_cond_initで初期化します。

pthread_cond_broadcast_main.c main関数 // 該当部分を抽出し記載
int main(int argc, FAR char *argv[])
{

  if (pthread_cond_init(&switch_cond_5, NULL) != 0) {
      printf("Error; pthread_cond_init.\n");
      exit(1);
  }
  
}

条件待ち

条件待ちはthread_cond_wait_5で実現します。シリーズ#3, 4と同じです。
ミューテックスロック・アンロックでthread_cond_waitを括ること、thread_cond_waitの引数にミューテックスを指定しています。

pthread_cond_broadcast_main.c thread_cond_wait_5関数 // 該当部分を抽出し記載
void* thread_cond_wait_5(void* arg) {
  int switch_index = (uint32_t)arg;

  printf("thread_cond_wait[%d]:cond_wait[%d]...\n", switch_index, switch_index);

  // 条件待ち
  pthread_mutex_lock(&switch_mutex_5);
  if (pthread_cond_wait(&switch_cond_5, &switch_mutex_5) != 0) {
      printf("Fatal error on pthread[%d]_cond_wait.\n", switch_index);
      exit(1);
  }
  pthread_mutex_unlock(&switch_mutex_5);

  printf("thread_cond_wait[%d]:wait end.\n", switch_index);

  return (void*)switch_index;
}

条件待ち解除

条件待ち解除はpthreadsのpthread_cond_broadcastで実現します。
APS学習ボードのスイッチ1が押下されたらpthread_cond_broadcastを実行し、引数の条件変数で待っているスレッドの待ち状態を全て解除します。

pthread_cond_broadcast_main.c thread_cond_broadcast関数 // 該当部分を抽出し記載
void* thread_cond_broadcast(void* arg) {
  int switch_index = (uint32_t)arg;

  volatile int switch_status = board_gpio_read(aps_board_switch_pin_5[switch_index]);

  while (switch_status == 1) {  // スイッチ押下されたらループから抜ける
    switch_status = board_gpio_read(aps_board_switch_pin_5[switch_index]);
    sleep(1);
  }

  pthread_cond_broadcast(&switch_cond_5);

  printf("thread_cond_broadcast[%d]:switch[%d] pushed!!!cond_signal set.\n", switch_index, switch_index + 1);

  return (void*)switch_index;
}

条件変数の削除

条件変数が不要になったらpthread_cond_destroyで削除します。シリーズ#3, 4と同じです。

pthread_cond_broadcast_main main関数 // 該当部分を抽出し記載

pthread_cond_t switch_cond_5;

int main(int argc, FAR char *argv[])
{

  if (pthread_cond_destroy(&switch_cond_5) != 0) {
      printf("Error; pthread_cond_destroy.\n");
      exit(1);
  }

  printf("Bye.\n");

  return 0;
}

動作確認結果

プログラムを実行すると3つのスレッド:thread_cond_wait_5は待ち状態になります。
APS学習ボードのスイッチ1を押下するとpthreadsのpthread_cond_broadcastで条件待ち解除しスレッドを終了ます。
3つのスレッド:thread_cond_wait_5は条件待ち解除のシグナルを受信すると条件待ち解除になりスレッドを終了します。
次はAPS学習ボードのスイッチ1を押下した場合のprintfです。

sh> pthread_cond_broadcast
thread_cond_wait[0]:cond_wait[0]...
thread_cond_wait[1]:cond_wait[1]...
thread_cond_wait[2]:cond_wait[2]...
thread_cond_broadcast[0]:switch[1] pushed!!!cond_signal set.
thread_cond_wait[0]:wait end.
thread_cond_wait[1]:wait end.
thread_cond_wait[2]:wait end.
main:thread_cond_broadcast[3] exit.return value = 0
main:thread_cond_wait[0] exit.return value = 0
main:thread_cond_wait[1] exit.return value = 1
main:thread_cond_wait[2] exit.return value = 2
Bye.

動作確認方法

シリーズ #1と同じです。
今回のアプリケーション名はpthread_cond_broadcastです。

今回の感想

1つの条件で複数スレッドが待っている場合にpthread_cond_broadcastを使用すれば複数スレッドの待ちを解除できることを確認できました。
複数の待ち状態を一度に解除できることは便利と思う反面、参考資料:Linuxとpthreadsによる マルチスレッドプログラミング入門[2]の記載が印象的でした。

  • 条件待ちをするスレッドは条件変数1つにつき1つだけという設計をすることを勧める
  • 1つのトリガで複数のスレッドの処理を動作開始させなければいけない場合でも、そのスレッドの数だけ条件変数を用意し、そのスレッドの数だけpthread_cond_signak関数を呼び出す作りにする。そうすれば条件待ちをするスレッドは条件変数1つにつき1つだけという設計にできるはず

シンプルな設計を心がけたいと感じました。

今回も最後まで読んでいただきありがとうございました。

脚注
  1. GitHubリンク ↩︎

  2. 書籍:Linuxとpthreadsによる マルチスレッドプログラミング入門のリンク ↩︎

Discussion