前回はスレッド間で通信するのにQueueを使ってみましたが、今回は似た「先入れ先出し」の構造でMailです。Mail便利です。こちらを使うとQueueには戻れない?便利なのは、詰め込むデータのためのメモリ領域の割り当て/解放の機能を含んでいるところ。alloc使うと後戻りできまっせん。
※「モダンOSのお砂場」投稿順Indexはこちら
※実験コードのビルドは、Arm社のWeb開発環境 Mbed Compilerを使わせていただいております。登録すればインストールせずに即使える開発環境です。実機ターゲットは、ST Microelectronics社のNucleo-F401REボード(STM32F401REマイコン搭載。コアは Arm Cortex M4F)です。
年寄りは、組み込みマイコンで動的なメモリ割り当て alloc をするのは気が咎めるのであります。静的に割り当てるのが当然と。なにせその昔はRAM容量が限られていたし、メモリ管理のオーバヘッドも馬鹿にならないから。でもま、このごろは組み込み用のマイコンのレベルが上がってそういう制限も緩んできたと。ホントか?
今回のMail構造は、前回のQueue構造と同様に複数スレッド間で通信する仕組みです。片方のスレッドからputしたデータが、別のスレッドでgetできると。その間にQueue同様に「記憶のFIFO」が設定されています。put/getのタイミングがちょっとバラバラでも設定段数の範囲内で前後するだけならばバッファされてスムースに通信できると。
最大の違いが、Queueに詰められるのがデータ実体を指すポインタであったのに比べて、Mailは入れ物を用意できること。Queueの場合、Queueとは別にデータ実体を管理する必要がありました。前回はことさらに静的に確保した配列を使ってみましたが、その管理がちょいと面倒。ところがMailの場合、送信元がallocで入れ物を確保し、受信先が使い終わったらfreeで解放すれば良いと。管理簡単です。
MbedのAPIのドキュメントの中の Mail の説明が以下に。
Scheduling: RTOS and event handling > RTOS APIs > Mail
実験用のコード
実験用のコードは、前回Queue構造で使ったものの使い回しです。スレッドT1からスレッドT2に向かってMail機構を使ってテストデータを送り、受信したスレッドT2はそれを標準出力に表示。メイン関数は初期化後は我関せずとLチカしつづける、です。
テストデータには iris なる構造体としました。別記事にひきずられてます。
#include "mbed.h" #define BLINKING_RATE_MS (500) #define MAILBOX_DEPTH (16) typedef struct { float sepal_length; float sepal_width; float petal_length; float petal_width; } iris_t; Mail<iris_t, MAILBOX_DEPTH> mailBox; Thread t1; Thread t2; void t1_thread() { int waitMS; while (true) { waitMS = (rand() & 0xFF) * 10; if (!mailBox.full()) { iris_t *iris = mailBox.alloc(); iris->sepal_length = (rand() & 0x1F) * 0.1; iris->sepal_width = (rand() & 0x1F) * 0.1; iris->petal_length = (rand() & 0x1F) * 0.1; iris->petal_width = (rand() & 0x1F) * 0.1; mailBox.put(iris); } ThisThread::sleep_for(waitMS); } } void t2_thread() { while (true) { osEvent evt = mailBox.get(); if (evt.status == osEventMail) { iris_t *iris = (iris_t *)evt.value.p; printf("Get: SL=%f SW=%f PL=%f PW=%f\r\n", iris->sepal_length, iris->sepal_width, iris->petal_length, iris->petal_width); mailBox.free(iris); } ThisThread::sleep_for(10); } } int main() { printf("\r\n\r\n*** MailBox Sample ***\r\n"); DigitalOut led(LED1); t1.start(t1_thread); t2.start(t2_thread); while (true) { led = !led; thread_sleep_for(BLINKING_RATE_MS); } }
ビルド結果
Mbedのウエブ環境のコンパイル結果が以下です。前回と比べると若干大きくなってますが、allocするからといって心配するほどではないです(個人の感想です。)食わず嫌い?そもそも入れるデータ量も限られているのだから。
実機動作結果
生成されたオブジェクトを Nucleo-F401REボードに書き込んで、標準出力に仮想シリアル端末(Teraterm)接続したときの様子を冒頭のアイキャッチ画像に掲げました。
予定どおり動いてますな。しかし、結果をみるとダメなところ発見。データは乱数で生成しているのですが、0を排除していなかったです。がく片の幅が0cmで長さが1.4cmとかありえないです。テストデータではあるけれど、もっともらしいデータにするならば0は排除しないと。それに計算上、小数点以下1桁しかありえないのに、成り行きの%f指定で0が多すぎ。iris 様に怒られそう。