ESP32でマルチスレッド：キューを送る

![SS 192.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/565043/a41cd967-f549-8677-1efa-2e99bf69518d.png) ESP32とArduinoIDEでキューを送りたい

#やること

[前回](https://qiita.com/Ninagawa_Izumi/items/5c3a9d40996836bd825f)の続きで、ESP32DevKitCとArduinoIDEを使って、ESP32でマルチコア・マルチスレッドを使っていきます。

複数のプロセスを並行処理するマルチスレッドでは、プロセス間でデータの受け渡しをする際にルールを決めておかないと、同じデータを同時に上書きするなどの問題が生じます。
そこで今回は**キューによるデータの受け渡し**を試してみます。

#そもそもキューとは
キュー(Queue)とは列のことです。
スレッド間で順次データを送信していくわけですが、キューの仕組みを使えば、もし受け取り先でデータの処理が遅れていた場合に、データを列に並べて待機させることができます。
処理が空き次第、待ち列の先頭からデータを相手にコピーで渡していきます。コピーが済んだ先頭のデータは消去され、次のデータが先頭に押し出されます。

<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/565043/723d7200-b289-5bc9-2c53-dd83dc491091.png" width = 400>
また、今回の説明のようにデータを先入れ先だしする方法のことを**FIFO**（フィフォ、First In First OUT）と言います。この用語もよく出てきます。


#準備
- Arduino IDEの入ったPC
- Arduino IDEでESP32が使えるようにしてある
- ESP32DevKitC

# Arduino IDEで試す
ではさっそくキューを試してみましょう

```cpp:xQueue_test.ino
TaskHandle_t thp[2];//マルチスレッドのタスクハンドル格納用
QueueHandle_t xQueue_1;//Queueを使う準備 [キューハンドル名]

void setup() {
  Serial.begin(115200);

  xQueue_1 = xQueueCreate( 10, 16 );
  //xQueueCreate( [待ち行列の席数], [一席あたりのバイト数] );

  //スレッドの準備
  xTaskCreatePinnedToCore(Core0a, "Core0a", 4096, NULL, 2, &thp[0], 0);
  xTaskCreatePinnedToCore(Core1a, "Core1a", 4096, NULL, 1, &thp[1], 1);
  //xTaskCreatePinnedToCore(
  //   [タスク名], "[タスク名]", 
  //   [スタックメモリサイズ(4096or8192)], [NULL], 
  //   [タスク優先順位(1-24)] 大きいほど優先順位が高い,
  //   [宣言したタスクハンドルのポインタ(&thp[0])], [CoreID(0or1)]); 
}

void loop() {//メインループ
  Serial.println("[ main ] loop");
  delay(100);
}

void Core1a(void *args) {//スレッド ①
  int a = 0;//送信データの変数
  while (1) {
    xQueueSend( xQueue_1, &a, 0 );
    //xQueueSend([キューハンドル名], [送る値], [待ち行列の席が空くまで待つ時間]);
    a++;
    delay(1000);
  }
}

void Core0a(void *args) {//スレッド ②
  int b = 0;//データ受信用の変数
  while (1) {
    /* メッセージ受信待ち */
    xQueueReceive( xQueue_1, &b, portMAX_DELAY );
    //xQueueSend([キューハンドル名], [データを受信するアドレス], [キュー空きを待つ最大時間。portMAX_DELAYで永久待ち])
    Serial.print("[Core0a] xQueueReceive:");
    Serial.println(b);
    delay(1);
  }
}

```

##### 実行結果

<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/565043/f4e2e148-04df-0067-131a-c0393a4f6d17.png" width = 200>
Core0aはdelay(1)間隔の高速ループでキューからの受信を待ち受けています。
一方で、Core1aはdelay(1000)のゆっくりとした間隔でキューを発信しています。
また、メインループはdelay(100)の間隔で更新されています。
このため、メインループ10回の表示につき、Core0aが受け取ったキューを1回表示するという実行結果になります。


# ポイント
・キュー先頭のデータは一度取得されると消えます。
　たとえばキューのデータが１つしかない場合に、２つのプロセスからキューのデータを取得しようとすると、最初にキューにアクセスしたプロセスだけがデータを取得できます。
・キューの繰り出しはOSが自動的に行ってくれます。
・キュー空きを待つ最大時間は、portMAX_DELAYで永久待ちとなります。
・送受信用の変数はスレッドの内部に持つようにします。（ローカルスコープ）

#参考URL
[FreeRTOSでマルチタスク (on ESP32)](https://qiita.com/hideakitai/items/bd95d0db63097b8808f9)
[Arduino – ESP32 のマルチタスク ( Dual Core ) を試す](https://www.mgo-tec.com/blog-entry-arduino-esp32-multi-task-dual-core-01.html)
[[M5Stack] マルチスレッドで並列処理を行う]
(https://qiita.com/kotai2003/items/6a4d620a51ca0f1535dc)
[ESP32のマルチタスクとCPU利用率を調べる](https://lang-ship.com/blog/work/esp32-multitasking/)
[ESP32のFreeRTOS入門 その2 タスクの作成](https://lang-ship.com/blog/work/esp32-freertos-l02-taskcreate/)
[xQueueReceive](http://www.azusa-st.com/kjm/FreeRtos/API/queues/xQueueReceive.html)
が大変参考になりました。ありがとうございました。

# 次回

次はセマフォの使い方を試してみます。

次回記事：

https://qiita.com/Ninagawa_Izumi/items/cc9b960535ec87b7c2e3

前回記事：

https://qiita.com/Ninagawa_Izumi/items/5c3a9d40996836bd825f