🐰

SpresenseでNuttXのpthreadsを使用しマルチスレッドを学ぶ #4 〜条件変数で【タイムアウト付き】条件待ちする〜

2022/03/25に公開

これは何?

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

今回のテーマ

前回は条件変数を使ってプログラムの待ち合わせが実現できました。
条件待ちは永久に待つコードでしたが、今回はタイムアウト付きの条件待ちを確認します。
今回使うpthreadsの関数は次のとおりです。

  • pthread_cond_init:条件変数の初期化
  • pthread_cond_timedwait:タイムアウト条件待ち ★今回初登場★
  • pthread_cond_signal:条件待ち解除
  • pthread_cond_destroy:条件変数の削除

前回は条件待ちをpthread_cond_waitで実現していましたが、今回はpthread_cond_timedwaitを使用しタイムアウト付き条件待ちを実現します。

確認環境

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

テストコード説明

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

プログラムの構成は前回と同じです。
条件待ちのロジックを変更しています。
thread_cond_timedwait_funcが条件待ちするスレッドです。
条件解除シグナル受信でスレッド終了、タイムアウト発生で再び条件待ちに入ります。
条件解除シグナルは前回と同じでAPS学習ボードのスイッチ1または2押下です。

pthread_cond_timedwait_msecは関数内部でpthread_cond_timeoutを呼び出し指定時間のタイムアウト条件待ちをしています。

条件変数の定義

前回と同じです。

条件変数の初期化

前回と同じです。

条件待ち

条件待ちを行うコードについて説明します。
条件待ちを行うスレッドthread_cond_timedwait_funcからpthreadsのpthread_cond_timedwaitを実行しているラッパー関数pthread_cond_timedwait_msecを呼び出しタイムアウト付き条件待ちをしています。

条件待ちスレッド thread_cond_timedwait_func

条件待ちスレッドthread_cond_timedwait_funcは次のコードです。
pthread_cond_timedwait_msec関数の戻り値で条件解除・タイムアウト・異常終了か判断しています。
条件解除の場合はスレッドを終了します。
タイムアウトの場合はpthread_cond_timedwait_msec関数を再度呼び出し条件待ちに入ります。
タイムアウト時間は10秒を指定しています。

pthread_cond_timeout_main.c thread_cond_timedwait_func関数 // 該当部分を抽出し記載
void* thread_cond_timedwait_func(void* arg) {
  int switch_index = (uint32_t)arg;
  volatile int stopRequest = 0;
  int ret;

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

  // タイムアウト条件待ち
  pthread_mutex_lock(&switch_mutex_timeout[switch_index]);
  while(!stopRequest) {
    ret = pthread_cond_timedwait_msec(&switch_cond_timeout[switch_index], &switch_mutex_timeout[switch_index], 10000);
    switch (ret)
    {
    case 0: /* got signal */
        stopRequest = 1;
        printf("thread_cond_wait[%d]:got signal.\n", switch_index);
        break;
    case ETIMEDOUT: /* timeout */
        printf("thread_cond_wait[%d]:timeout.\n", switch_index);
        break;
    default:        /* fatal error */
        printf("thread_cond_wait[%d]:Fatal error(%d) on pthread_cond_timedwait.\n", switch_index, ret);
        exit(1);
    }
  }
  pthread_mutex_unlock(&switch_mutex_timeout[switch_index]);

  return (void*)switch_index;
}

ラッパー関数 pthread_cond_timedwait_msec

ラッパー関数pthread_cond_timedwait_msecは次のコードです。
引数のタイムアウト値(ミリ秒単位)からtimespec構造体のフォーマットに設定し、pthreadsのpthread_cond_timedwaitに設定・呼び出します。
この関数は書籍:Linuxとpthreadsによる マルチスレッドプログラミング入門[2]を参考にしました。書籍との違いは時間を取得する関数(clock_systime_timespec)です。

pthread_cond_timeout_main.c pthread_cond_timedwait_msec関数 // 該当部分を抽出し記載
/*
* ミリ秒単位で条件待ちをする
*/
int pthread_cond_timedwait_msec(pthread_cond_t* cond,
    pthread_mutex_t* mutex, long msec) {
    int ret;
    struct timespec ts;
    ret = clock_systime_timespec(&ts);
    if(ret != 0) {
      printf("Error; clock_systime_timespec[%d].\n", ret);
      exit(1);
    }
    ts.tv_sec += msec / 1000;
    ts.tv_nsec += (msec % 1000) * 1000000;
    if (ts.tv_nsec >= 1000000000) {
        ts.tv_sec++;
        ts.tv_nsec -= 1000000000;
    }
    return pthread_cond_timedwait(cond, mutex, &ts);
}

条件待ち解除

前回と同じです。

条件変数の削除

前回と同じです。

動作確認結果

プログラムを実行するとthread_cond_timedwait_funcは待ち状態になります。
10秒間APS学習ボードのスイッチ1または2を押下がないとタイムアウトのprintfが出力されます。
APS学習ボードのスイッチ1または2を押下があるとthread_cond_timedwait_funcは待ち状態が解除されスレッドを終了します。
次はAPS学習ボードのスイッチ1→スイッチ2の順番にスイッチを押下した場合のprintfです。

nsh> pthread_cond_timeout
thread_cond_wait[0]:cond_wait[0]...
thread_cond_wait[1]:cond_wait[1]...
thread_cond_wait[0]:timeout.
thread_cond_wait[1]:timeout.
thread_cond_wait[0]:timeout.
thread_cond_wait[1]:timeout.
thread_cond_wait[0]:timeout.
thread_cond_wait[1]:timeout.
thread_cond_wait[0]:timeout.
thread_cond_wait[1]:timeout.
thread_cond_wait[0]:timeout.
thread_cond_wait[1]:timeout.
thread_cond_signal[0]:switch[1] pushed!!!cond_signal[0] set.
thread_cond_wait[0]:got signal.
main:thread_cond_signal[0] exit.return value = 0
thread_cond_wait[1]:timeout.
thread_cond_wait[1]:timeout.
thread_cond_wait[1]:timeout.
thread_cond_signal[1]:switch[2] pushed!!!cond_signal[1] set.
thread_cond_wait[1]:got signal.
main:thread_cond_signal[1] exit.return value = 1
main:thread_cond_wait[0] exit.return value = 0
main:thread_cond_wait[1] exit.return value = 1
Bye.

次はAPS学習ボードのスイッチ2→スイッチ1の順番にスイッチを押下した場合のprintfです。
main関数はthread_cond_signal[0]の終了を待つようにしているのでスイッチ1→スイッチ2の時とprintfの表示順番が違っています。

nsh> pthread_cond_timeout
thread_cond_wait[0]:cond_wait[0]...
thread_cond_wait[1]:cond_wait[1]...
thread_cond_wait[0]:timeout.
thread_cond_wait[1]:timeout.
thread_cond_wait[0]:timeout.
thread_cond_wait[1]:timeout.
thread_cond_signal[1]:switch[2] pushed!!!cond_signal[1] set.
thread_cond_wait[1]:got signal.
thread_cond_wait[0]:timeout.
thread_cond_wait[0]:timeout.
thread_cond_wait[0]:timeout.
thread_cond_wait[0]:timeout.
thread_cond_wait[0]:timeout.
thread_cond_signal[0]:switch[1] pushed!!!cond_signal[0] set.
thread_cond_wait[0]:got signal.
main:thread_cond_signal[0] exit.return value = 0
main:thread_cond_signal[1] exit.return value = 1
main:thread_cond_wait[0] exit.return value = 0
main:thread_cond_wait[1] exit.return value = 1
Bye.

動作確認方法

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

今回の感想

指定時間経過でタイムアウト発生、条件解除シグナルの受信で条件待ち解除が確認できました。
タイムアウト付き条件待ちを使うことで実用的なマルチスレッドのアプリケーションがつくれそうですね。

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

脚注
  1. GitHubリンク ↩︎

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

Discussion