複数の実行中のスレッドを作成するにはどうすればよいですか?

同じコードブロックで複数のことを行わずに、プログラムの複数の部分を一緒に実行する方法はありますか?

1つ別のスレッドでLEDを点滅させながら、外部デバイスを待機しているスレッド。

コメント

  • 最初に、本当にスレッドが必要かどうかを自問する必要があります。タイマーはすでにニーズに合っている可能性があり、Arduinoでネイティブにサポートされています。
  • Uzeboxもチェックしてみてください。 ‘は2チップの自作ビデオゲームコンソールです。したがって、’は正確にはArduinoではありませんが、システム全体は割り込みに基づいて構築されています。そのため、オーディオ、ビデオ、コントロールなどはすべて割り込み駆動型ですが、メインプログラムは’それらについて心配する必要はありません。良い参考になるかもしれません。

回答

マルチプロセスやマルチスレッドのサポートはありません。 Arduino。ただし、一部のソフトウェアを使用して、複数のスレッドに近いことを行うことができます。

プロトスレッドを確認する:

プロトスレッドは、小さな組み込みシステムやワイヤレスセンサーネットワークノードなど、メモリに深刻な制約があるシステム向けに設計された非常に軽量なスタックレススレッドです。プロトスレッドは、Cで実装されたイベント駆動型システムに線形コード実行を提供します。プロトスレッドは、基盤となるオペレーティングシステムの有無にかかわらず使用して、ブロッキングイベントハンドラーを提供できます。プロトスレッドは、複雑なステートマシンや完全なマルチスレッドなしで制御のシーケンシャルフローを提供します。

もちろん、Arduinoの例ここサンプルコード。この SOの質問も役立つかもしれません。

ArduinoThread

コメント

回答

AVRベースのArduinoは(ハードウェア)スレッドをサポートしていません。私はARMベースのArduinoに慣れていません。この制限を回避する1つの方法は、割り込み、特に時限割り込みを使用することです。タイマーをプログラムして、メインルーチンを非常に多くのマイクロ秒ごとに中断し、特定の他のルーチンを実行することができます。

http://arduino.cc/en/Reference/Interrupts

回答

Unoでソフトウェア側のマルチスレッドを実行することができます。ハードウェアレベルのスレッド化はサポートされていません。

マルチスレッド化を実現するには、基本的なスケジューラを実装し、実行する必要のあるさまざまなタスクを追跡するためのプロセスまたはタスクリストを維持する必要があります。

非常に単純な非プリエンプティブスケジューラの構造は次のようになります。

//Pseudocode void loop() { for(i=o; i<n; i++) run(tasklist[i] for timelimit): } 

ここでは、tasklistは関数ポインタの配列にすることができます。

tasklist [] = {function1, function2, function3, ...} 

フォームの各関数:

int function1(long time_available) { top: //Do short task if (run_time<time_available) goto top; } 

各関数は、function1 LED操作の実行、function2フロート計算の実行などの個別のタスクを実行できます。割り当てられた時間を守るのは各タスク(機能)の責任です。

うまくいけば、これで開始できます。

コメント

  • “スレッド非プリエンプティブスケジューラを使用する場合。ちなみに、このようなスケジューラはすでにarduinoライブラリとして存在します: arduino.cc/en/Reference/Scheduler
  • @ jfpoilpret-協調マルチスレッドは本物です。
  • はい’正解です!私の間違い。ずっと前に、協調マルチスレッドに直面していなかったので、頭の中でマルチスレッドはプリエンプティブでなければなりませんでした。

回答

要件の説明に従って:

  • 外部デバイスを待機している1つのスレッド
  • LEDを点滅させている1つのスレッド

最初の「スレッド」に1つのArduino割り込みを使用できるようです(実際には「タスク」と呼びたいです)。

Arduino割り込みは、外部に基づいて1つの関数(コード)を呼び出すことができます。イベント(電圧レベルまたはデジタル入力ピンのレベル変更)。これにより、関数がすぐにトリガーされます。

ただし、割り込みで注意すべき重要な点の1つは、呼び出される関数をできるだけ高速にする必要があることです。 (通常、delay()呼び出しやdelay()に依存するその他のAPIは使用しないでください)。

外部イベントトリガー時にアクティブ化するタスクが長い場合は、協調スケジューラを使用して、割り込み関数から新しいタスクを追加する可能性があります。

2番目に重要な割り込みについてのポイントは、それらの数が制限されていることです(たとえば、UNOでは2つだけ)。したがって、より多くの外部イベントが発生し始めた場合は、すべての入力を1つに多重化する何らかの実装を行い、割り込み関数に、実際のトリガーである多重化された入力を判別させる必要があります。

回答

簡単な解決策は、スケジューラーを使用することです。いくつかの実装があります。これは、AVRおよびSAMベースのボードで利用できるものについて簡単に説明します。基本的に、1回の呼び出しでタスクが開始されます。 「スケッチ内のスケッチ」。

#include <Scheduler.h> .... void setup() { ... Scheduler.start(taskSetup, taskLoop); } 

Scheduler.start()は、taskSetupを1回実行してから、同じようにtaskLoopを繰り返し呼び出す新しいタスクを追加します。 Arduinoスケッチは機能します。タスクには独自のスタックがあります。スタックのサイズはオプションのパラメーターです。デフォルトのスタックサイズは128バイトです。

コンテキスト切り替えを許可するには、タスクで yield()または delay(を呼び出す必要があります。 )。条件を待機するためのサポートマクロもあります。

await(Serial.available()); 

マクロは次の構文糖衣構文です:

while (!(Serial.available())) yield(); 

待機することもできますタスクを同期するために使用されます。以下にスニペットの例を示します。

volatile int taskEvent = 0; #define signal(evt) do { await(taskEvent == 0); taskEvent = evt; } while (0) ... void taskLoop() { await(taskEvent); switch (taskEvent) { case 1: ... } taskEvent = 0; } ... void loop() { ... signal(1); } 

詳細については、の例を参照してください。複数のLEDの点滅からデバウンスボタンまでの例と、ノンブロッキングコマンドライン読み取りを備えた単純なシェルがあります。テンプレートと名前空間を使用して、ソースコードの構造化と削減に役立てることができます。 スケッチの下に、マルチ点滅用のテンプレート関数の使用方法を示します。スタックには64バイトで十分です。

#include <Scheduler.h> template<int pin> void setupBlink() { pinMode(pin, OUTPUT); } template<int pin, unsigned int ms> void loopBlink() { digitalWrite(pin, HIGH); delay(ms); digitalWrite(pin, LOW); delay(ms); } void setup() { Scheduler.start(setupBlink<11>, loopBlink<11,500>, 64); Scheduler.start(setupBlink<12>, loopBlink<12,250>, 64); Scheduler.start(setupBlink<13>, loopBlink<13,1000>, 64); } void loop() { yield(); } 

ベンチマークもあり、パフォーマンス、つまり時間のアイデアを提供します。タスクの開始、コンテキストスイッチなど。

最後に、タスクレベルの同期と通信のためのサポートクラスがいくつかあります。 キューセマフォ

回答

マトリックスLEDディスプレイの実装中にこのトピックにも行きました。

一言で言えば、Arduinoでmillis()関数とタイマー割り込みを使用してポーリングスケジューラを構築できます。

BillEarlからの次の記事をお勧めします:

https://learn.adafruit.com/multi-tasking-the-arduino-part-1/overview

https://learn.adafruit.com/multi-tasking-the-arduino-part-2/overview

https://learn.adafruit.com/multi-tasking-the-arduino-part-3/overview

回答

このフォーラムの前回の質問から、次の質問/回答が電気工学に移されました。メインループを使用してシリアルIOを実行しているときにタイマー割り込みを使用してLEDを点滅させるサンプルarduinoコードがあります。

https://electronics.stackexchange.com/questions/67089/how-can-i-control-things-without-using-delay/67091#67091

再投稿:

割り込みは、何か他のことが起こっている間に物事を成し遂げるための一般的な方法です。以下の例では、delay()を使用せずにLEDが点滅しています。 Timer1が起動するたびに、割り込みサービスルーチン(ISR)isrBlinker()が呼び出されます。 LEDのオン/オフを切り替えます。

他のことが同時に発生する可能性があることを示すために、loop()は、LEDの点滅とは関係なく、foo / barをシリアルポートに繰り返し書き込みます。 。

#include "TimerOne.h" int led = 13; void isrBlinker() { static bool on = false; digitalWrite( led, on ? HIGH : LOW ); on = !on; } void setup() { Serial.begin(9600); Serial.flush(); Serial.println("Serial initialized"); pinMode(led, OUTPUT); // initialize the ISR blinker Timer1.initialize(1000000); Timer1.attachInterrupt( isrBlinker ); } void loop() { Serial.println("foo"); delay(1000); Serial.println("bar"); delay(1000); } 

これは非常に単純なデモです。 ISRははるかに複雑になる可能性があり、タイマーと外部イベント(ピン)によってトリガーされる可能性があります。一般的なライブラリの多くはISRを使用して実装されています。

回答

ThreadHandlerライブラリを試してみることもできます

https://bitbucket.org/adamb3_14/threadhandler/src/master/

割り込みスケジューラを使用して、リレーせずにコンテキストの切り替えを可能にしますyield()またはdelay()。

ライブラリを作成したのは、3つのスレッドが必要であり、他のスレッドが何をしているかに関係なく、正確な時間に実行するために2つのスレッドが必要だったためです。最初のスレッドはシリアル通信を処理しました。 2つ目は、Eigenライブラリでfloat行列の乗算を使用してカルマンフィルターを実行することでした。そして3つ目は、行列の計算を中断できなければならない高速電流制御ループスレッドでした。

仕組み

各循環スレッドには優先順位と期間があります。現在実行中のスレッドよりも優先度の高いスレッドが次の実行時間に達すると、スケジューラーは現在のスレッドを一時停止し、優先度の高いスレッドに切り替えます。優先度の高いスレッドが実行を完了すると、スケジューラーは前のスレッドに戻ります。

スケジューリングルール

ThreadHandlerライブラリのスケジューリングスキームは次のとおりです。

  1. 最も高い優先度が最初になります。
  2. 優先度が同じ場合は、期限が最も早いスレッドが最初に実行されます。
  3. 2つのスレッドの期限が同じ場合は、最初に作成されたスレッドが最初に実行されます。
  4. スレッドは、優先度の高いスレッドによってのみ中断できます。
  5. スレッドが実行されると、実行関数が戻るまで、優先度の低いすべてのスレッドの実行がブロックされます。
  6. ループ関数の優先度はThreadHandlerスレッドと比較して-128です。

使用方法

スレッドはc ++継承を介して作成できます

 class MyThread : public Thread { public: MyThread() : Thread(priority, period, offset){} virtual ~MyThread(){} virtual void run() { //code to run } }; MyThread* threadObj = new MyThread();  

またはcreateThreadとラムダ関数を介して

 Thread* myThread = createThread(priority, period, offset, []() { //code to run });  

スレッドオブジェクトは、作成時に自動的にThreadHandlerに接続します。

作成されたスレッドオブジェクトの実行を開始するには、次の呼び出しを行います。

 ThreadHandler::getInstance()->enableThreadExecution();  

swer

さらに別のマイクロプロセッサ協調マルチタスクライブラリ– PQRST:単純なタスクを実行するための優先キュー。

この中モデルでは、スレッドはTaskのサブクラスとして実装されます。これは、将来のある時間にスケジュールされます(また、一般的なように、サブクラス

)。オブジェクトのrun()メソッドは、タスクの期限が来ると呼び出されます。 run()メソッドは、適切な作業を行ってから戻ります(これは協調ビットです)。通常、連続する呼び出しでのアクションを管理するために、ある種のステートマシンを維持します(簡単な例は、以下の例のlight_on_p_変数です)。これには、方法を少し再考する必要があります。コードを整理しますが、かなり集中的に使用すると非常に柔軟で堅牢であることが証明されています。

時間単位にとらわれないため、millis() as micros()、またはその他の便利なティック。

このライブラリを使用して実装された「blink」プログラムは次のとおりです。これは、実行中のタスクが1つだけであることを示しています。通常、他のタスクはsetup()内で作成および開始されます。

#include "pqrst.h" class BlinkTask : public LoopTask { private: int my_pin_; bool light_on_p_; public: BlinkTask(int pin, ms_t cadence); void run(ms_t) override; }; BlinkTask::BlinkTask(int pin, ms_t cadence) : LoopTask(cadence), my_pin_(pin), light_on_p_(false) { // empty } void BlinkTask::run(ms_t t) { // toggle the LED state every time we are called light_on_p_ = !light_on_p_; digitalWrite(my_pin_, light_on_p_); } // flash the built-in LED at a 500ms cadence BlinkTask flasher(LED_BUILTIN, 500); void setup() { pinMode(LED_BUILTIN, OUTPUT); flasher.start(2000); // start after 2000ms (=2s) } void loop() { Queue.run_ready(millis()); } 

コメント

  • これらは「実行から完了まで」のタスクですよね?
  • @EdgarBonet I ‘あなたが何を意味するのかよくわかりません。 run()メソッドが呼び出された後、メソッドは中断されないため、合理的に迅速に終了する責任があります。ただし、通常は、’作業を行ってから、自分自身を再スケジュールします(LoopTaskのサブクラスの場合は自動的に)未来の時間。一般的なパターンは、タスクが内部ステートマシンを維持することです(簡単な例は上記のlight_on_p_状態です)。これにより、次の期限が来たときに適切に動作します。
  • はい、これらは実行完了(RtC)タスクです。現在のタスクがrun()から戻って実行を完了するまで、タスクを実行することはできません。これは、たとえばyield()またはdelay()を呼び出すことでCPUを生成できる協調スレッドとは対照的です。または、いつでもスケジュールアウトできるプリエンプティブスレッド。スレッドを検索するためにここに来る多くの人々は、ステートマシンよりもブロッキングコードを書くことを好むので、区別が重要だと感じています。 CPUを生成する実際のスレッドをブロックすることは問題ありません。 RtCタスクをブロックすることはできません。
  • @EdgarBonet ‘は便利な違いです。このスタイルとyieldスタイルのスレッドはどちらも、プリエンプティブスレッドとは対照的に、単に異なるスタイルの協調スレッドと見なしますが、’には異なるアプローチが必要であることは事実です。それらをコーディングします。ここで言及されているさまざまなアプローチの思慮深く詳細な比較を見るのは興味深いでしょう。上記に記載されていない優れたライブラリの1つは、プロトスレッドです。私は両方で批判することだけでなく、賞賛することも見つけます。私は(もちろん)私のアプローチを好みます。なぜなら、それは最も明確に見え、余分なスタックを必要としないからです。
  • (訂正:プロトスレッドで言及されました。 @sachleen ‘の回答

コメントを残す

メールアドレスが公開されることはありません。 * が付いている欄は必須項目です