
今回はQueueを使ってみたいと思います。第34回で似たお名前のEventQueueというものを使ってみましたが、全く異なるものです。QueueはスレッドやISR間の通信のための仕組みで、EventQueueはノンプリエンプティブな制御権の移動の仕組みです。「よくある」Queueだと思ってやってみたのだけれど疑問あり。何故?
※「モダンOSのお砂場」投稿順Indexはこちら
※実験コードのビルドは、Arm社のWeb開発環境 Mbed Compilerを使わせていただいております。登録すればインストールせずに即使える開発環境です。実機ターゲットは、ST Microelectronics社のNucleo-F401REボード(STM32F401REマイコン搭載。コアは Arm Cortex M4F)です。
Queue
Arm Mbed OS6のQueueは、並行に走っている複数スレッド(Mbed OSではタスクでなくスレッドと呼びますです)間で通信するための仕組みです。Queueの一方の端から入れた情報が、他端から、基本は先入れ先出し方式で取り出すことができます(プライオリティ制御も可能とな。使ってみてないケド。)Mbed OS6には、似た機能のAPIにMail (BOX)という仕組みもあるようです。通信の実体メッセージまで格納することができるMailの仕組みと比べると、Queueは、実体メッセージの置き場所は管理してくれず、ポインタのみの格納です。通常はメモリをアロケートするような仕組みと組わせて使うみたいです(今回は「静的」にやっつけてしまいましたが。)
以下がMbed 公式の Queueに関するドキュメントのページです。
※疑問があるのよ。
キューにデータを出し入れするコードを書くのにドキュメントを読み、以下のように認識しました。
-
- exampleコードでは、put() / get() を使っている
- しかしAPIドキュメントを見ると put() / get() 以外に try_put() / try_get() などのtry_からはじまるメソッド群がある。こちらの方が使い易そうに見える。
- また、以下のヘッダファイルの定義をみると put() / get() は推奨されていない印象を受ける。try_系を使う方が良いように思える。
上記のヘッダファイルから1箇所引用させていただくと、以下のようです。
MBED_DEPRECATED_SINCE(“mbed-os-6.0.0”, “Replaced with try_put and try_put_for. In future put will be an untimed blocking call.”)
しかし、try_put()使ってみたら、そんなメソッド知らない、と言われてしまいました。エラー。Mbed OSのリビジョンを確認してみましたが、現行の最新版である筈のv6.15でした。何故にtry_チョメチョメが使えない?未だに不明です。
しかたないので、以下のコードは、旧式?の put() / get() で書いてます。
実験用のコード
実験は以下のようです。
-
- タスク1は、疑似乱数で10から2550までの10の倍数を生成する。QUEUEを調べてフルでなければ、生成した数をQUEUEにput()する。put()した数字は標準出力に報告する。QUEUEにput出来ても出来なくても生成した数字ミリ秒だけ待つ。ランダムな間隔でputとその表示が起こる。
- タスク2は、毎10ミリ秒毎にQUEUEをチェックし、QUEUEが空でなければQUEUEからデータを取り出す。getした数字を標準出力に報告する。
- main()関数は、上記2つのタスクを起動後、無関係にLチカしつづける。
#include "mbed.h"
#include "platform/mbed_thread.h"
#include "stdlib.h"
#define BLINKING_RATE_MS (500)
#define QUEUE_DEPTH (8)
Queue<int, QUEUE_DEPTH> qu;
int dataBuffer[QUEUE_DEPTH];
int dataIdx;
Thread t1;
Thread t2;
void t1_thread()
{
int waitMS;
while (true) {
waitMS = (rand() & 0xFF) * 10;
dataBuffer[dataIdx] = waitMS;
if (!qu.full()) {
qu.put(&(dataBuffer[dataIdx]));
printf("Put: %d\r\n",dataBuffer[dataIdx]);
}
dataIdx = dataIdx < (QUEUE_DEPTH - 1) ? dataIdx + 1 : 0;
ThisThread::sleep_for(waitMS);
}
}
void t2_thread()
{
while (true) {
if (!qu.empty()) {
osEvent evt = qu.get();
int *mess = (int *)evt.value.p;
printf("Get: %d\r\n",*mess);
}
ThisThread::sleep_for(10);
}
}
int main()
{
printf("\r\n\r\n*** Queue Sample ***\r\n");
dataIdx = 0;
DigitalOut led(LED1);
t1.start(t1_thread);
t2.start(t2_thread);
while (true) {
led = !led;
thread_sleep_for(BLINKING_RATE_MS);
}
}
ビルド結果
上記コードのMbed Compilerによるビルド結果は以下のようです。
ここでも、ちょっとムムムな感じ。
第39回、第40回と、Mbed OS6のオブジェクトを生成してますが、それらの回では「あまっているRAMはだいたい」ZI Dataで埋まっているように見えてました。しかし、今回はそういう感じはないです。こちらの方が普通な感じではあるのだけれど、使うAPIによってメモリの使い方が大きく異なる理由が知りたい!
ま、ビルドそのものは成功し、オブジェクトが得られたので、Nucleo-F401REに書き込んで動かしてみます。
実機動作結果
冒頭のアイキャッチ画像に、Nucleo-F401REの標準出力に接続した仮想端末の様子を掲げました。乱数で生成されたと思われる10ms単位の数字をPut して Get して期待どおりの挙動であります。
しかし、なんだかな~ 釈然としないのう。

