🐮

SpresenseでNuttXのpthreadsを使用しマルチスレッドを学ぶ #2 〜ミューテックスでプログラムの実行を排他制御〜

2022/03/02に公開

これは何?

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

今回のテーマ

今回はミューテックスを使ってプログラムの実行を排他制御します。
今回使うpthreadsの関数は次のとおりです。

  • pthread_mutex_init:ミューテックスの初期化
  • pthread_mutex_lock:ミューテックスのロック
  • pthread_mutex_unlock:ミューテックスのアンロック
  • pthread_mutex_destroy:ミューテックスの削除

確認環境

前回と同じです。

テストコード説明

今回のテストコードはGitHub[1]におきました。
ディレクトリ・実行ファイル名称はpthread_mutexです。
前回の【スレッドの起動・終了待ち】のサンプルコードをベースにミューテックスの制御を追加しています。

ミューテックスの定義

ミューテックスを定義します。

pthread_mutex_main.c // 該当部分を抽出し記載
pthread_mutex_t using_blink_led;

ミューテックスの初期化

ミューテックスを利用するにはpthread_mutex_initで初期化します。

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

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

ミューテックスのロック・アンロック

ミューテックスのロックはpthread_mutex_lockです。
ミューテックスのアンロックはpthread_mutex_unlockです。
pthread_mutex_lockとpthread_mutex_unlockで括られたコードが排他で実行される想定です。

pthread_mutex_main.c main関数 // 該当部分を抽出し記載
void* blink_led_mutex(void* arg) {
  uint32_t led_index = (uint32_t)arg;
  uint32_t switch_index = (uint32_t)arg;
  unsigned int sleep_sec = 1 + (uint32_t)arg; // sleepする秒数
  int led_value = 1;

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

  printf("start blink_led[%ld] thread ID = %d switch_status = %d.\n", led_index, pthread_self(), switch_status);

  while (switch_status == 1) {  // スイッチ押下されたらループから抜ける
    pthread_mutex_lock(&using_blink_led);

    board_gpio_write(aps_board_led_pin_2[led_index], led_value);
    sleep(sleep_sec);
    led_value ^= 1;
    switch_status = board_gpio_read(aps_board_switch_pin_2[switch_index]);
    printf("blink_led[%ld] thread ID = %d led_value = %d switch_status = %d.\n", led_index, pthread_self(), led_value, switch_status);

    pthread_mutex_unlock(&using_blink_led);
  }

  printf("end blink_led[%ld] thread ID = %d.\n", led_index, pthread_self());

  return (void*)led_index;
}

ミューテックスの削除

ミューテックスが不要になったらpthread_mutex_destroyで削除します。

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


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

  printf("Bye.\n");

  return 0;  
}

動作確認結果

今回のアプリケーションは前回の【スレッドの起動・終了待ち】のサンプルコードをベースにミューテックスの制御を追加しています。
printfの出力を比較してミューテックスによるプログラムの実行排他制御ができているか確認します。
今回(ミューテックス版)のprintfは次のとおりです。

nsh> pthread_mutex
start blink_led[0] thread ID = 12 switch_status = 1.               
start blink_led[1] thread ID = 13 switch_status = 1.               
blink_led[0] thread ID = 12 led_value = 0 switch_status = 1.
blink_led[1] thread ID = 13 led_value = 0 switch_status = 1.
blink_led[0] thread ID = 12 led_value = 1 switch_status = 1.
blink_led[1] thread ID = 13 led_value = 1 switch_status = 1.
blink_led[0] thread ID = 12 led_value = 0 switch_status = 1.
blink_led[1] thread ID = 13 led_value = 0 switch_status = 1.
blink_led[0] thread ID = 12 led_value = 1 switch_status = 1.
blink_led[1] thread ID = 13 led_value = 1 switch_status = 1.
blink_led[0] thread ID = 12 led_value = 0 switch_status = 1.
blink_led[1] thread ID = 13 led_value = 0 switch_status = 1.
blink_led[0] thread ID = 12 led_value = 1 switch_status = 1.
blink_led[1] thread ID = 13 led_value = 1 switch_status = 1.
blink_led[0] thread ID = 12 led_value = 0 switch_status = 1.
blink_led[1] thread ID = 13 led_value = 0 switch_status = 1.
blink_led[0] thread ID = 12 led_value = 1 switch_status = 1.
blink_led[1] thread ID = 13 led_value = 1 switch_status = 1.
blink_led[0] thread ID = 12 led_value = 0 switch_status = 1.
blink_led[1] thread ID = 13 led_value = 0 switch_status = 1.
blink_led[0] thread ID = 12 led_value = 1 switch_status = 1.
blink_led[1] thread ID = 13 led_value = 1 switch_status = 1.
blink_led[0] thread ID = 12 led_value = 0 switch_status = 1.
blink_led[1] thread ID = 13 led_value = 0 switch_status = 1.
blink_led[0] thread ID = 12 led_value = 1 switch_status = 1.
blink_led[1] thread ID = 13 led_value = 1 switch_status = 1.
blink_led[0] thread ID = 12 led_value = 0 switch_status = 1.
blink_led[1] thread ID = 13 led_value = 0 switch_status = 1.
blink_led[0] thread ID = 12 led_value = 1 switch_status = 1.
blink_led[1] thread ID = 13 led_value = 1 switch_status = 1.
blink_led[0] thread ID = 12 led_value = 0 switch_status = 1.
blink_led[1] thread ID = 13 led_value = 0 switch_status = 1.
blink_led[0] thread ID = 12 led_value = 1 switch_status = 1.
blink_led[1] thread ID = 13 led_value = 1 switch_status = 1.
blink_led[0] thread ID = 12 led_value = 0 switch_status = 1.
blink_led[1] thread ID = 13 led_value = 0 switch_status = 1.
blink_led[0] thread ID = 12 led_value = 1 switch_status = 0.
end blink_led[0] thread ID = 12.                                            
exit thread[0] thread ID = 11 return value = 0                       
blink_led[1] thread ID = 13 led_value = 1 switch_status = 1.
blink_led[1] thread ID = 13 led_value = 0 switch_status = 0.
end blink_led[1] thread ID = 13.                                            
exit thread[1] thread ID = 11 return value = 1                       
Bye.

前回(非ミューテックス版)のprintfは次のとおりです。

nsh> pthread_first_test_app
start blink_led[0] thread ID = 9 switch_status = 1.
start blink_led[1] thread ID = 10 switch_status = 1.
blink_led[0] thread ID = 9 led_value = 0 switch_status = 1.
blink_led[1] thread ID = 10 led_value = 0 switch_status = 1.
blink_led[0] thread ID = 9 led_value = 1 switch_status = 1.
blink_led[0] thread ID = 9 led_value = 0 switch_status = 1.
blink_led[1] thread ID = 10 led_value = 1 switch_status = 1.
blink_led[0] thread ID = 9 led_value = 1 switch_status = 1.
blink_led[0] thread ID = 9 led_value = 0 switch_status = 1.
blink_led[1] thread ID = 10 led_value = 0 switch_status = 1.
blink_led[0] thread ID = 9 led_value = 1 switch_status = 1.
blink_led[0] thread ID = 9 led_value = 0 switch_status = 1.
blink_led[1] thread ID = 10 led_value = 1 switch_status = 1.
end blink_led[0] thread ID = 9.
exit thread[0] thread ID = 8 return value = 0
blink_led[1] thread ID = 10 led_value = 1 switch_status = 1.
blink_led[1] thread ID = 10 led_value = 0 switch_status = 1.
blink_led[1] thread ID = 10 led_value = 1 switch_status = 1.
blink_led[1] thread ID = 10 led_value = 0 switch_status = 1.
blink_led[1] thread ID = 10 led_value = 1 switch_status = 1.
blink_led[1] thread ID = 10 led_value = 0 switch_status = 1.
blink_led[1] thread ID = 10 led_value = 1 switch_status = 1.
blink_led[1] thread ID = 10 led_value = 0 switch_status = 1.
blink_led[1] thread ID = 10 led_value = 1 switch_status = 1.
blink_led[1] thread ID = 10 led_value = 0 switch_status = 0.
end blink_led[1] thread ID = 10.
exit thread[1] thread ID = 8 return value = 1
Bye.

スレッド0(blink_led[0])が1秒間隔、スレッド1(blink_led[1])が2秒間隔でLEDが点滅するタスクでした。
前回・今回のprintfを比較すると次の違いがありました。

  • 前回はスレッド1のprintfの間にスレッド0のprintfが2つ確認できる。
  • 今回は前回のprintf出力パターンはなく、スレッド0・スレッド1のprintfが交互に出力されている。

ミューテックスのロック、アンロックのコードを追加することにより、スレッド1の2秒間のsleep中にスレッド0が実行していないことがわかります。
スレッド間のプログラム実行の排他制御が確認できました。

動作確認方法

前回と同じです。
前回とアプリケーション名は異なります。今回のアプリケーション名はpthread_mutexです。

今回の感想

SpresenseでNuttXのpthreadsのミューテックスの動作を確認することができました。
今回も前回と同様にホストPCのpthreadsと同じインターフェースで実装できました。
今後もマルチスレッドの同期処理について学習・実験していきたいと考えています。

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

脚注
  1. GitHubリンク ↩︎

Discussion