Hogyan hozhatok létre több futó szálat?

Van-e mód arra, hogy a program több része együtt fusson anélkül, hogy több dolgot végeznék ugyanabban a kódblokkban?

Egy külső eszközre váró szál, miközben egy másik szál LED-je is villog.

Megjegyzések

  • Valószínűleg először feltenné magának a kérdést, hogy valóban szüksége van-e szálakra. Lehet, hogy az időzítők már megfelelnek az Ön igényeinek, és az Arduino natív módon támogatja őket.
  • Érdemes megnézni az Uzebox-ot is. ‘ egy két chipes házi videojáték-konzol. Tehát bár nem ‘ t pontosan Arduino, a teljes rendszer megszakításokra épül. Tehát az audio, a videó, a kezelőszervek stb. Mind megszakításvezéreltek, miközben a fő programnak nem kell aggódnia ezekért. Jó referencia lehet.

Válasz

Nincs többfolyamatos, sem többszálas támogatás az Arduino. Bár néhány szoftverrel több szálhoz is hasonlót tehet.

Meg szeretné nézni a Protothreads elemeket:

A prototálak rendkívül könnyű, verem nélküli szálak, amelyeket erősen memóriahiányos rendszerek, például kis beágyazott rendszerek vagy vezeték nélküli érzékelő hálózati csomópontok számára terveztek. A protothreadek lineáris kódfuttatást biztosítanak a C-ben megvalósított eseményvezérelt rendszerek számára. A protreadreads az alapul szolgáló operációs rendszerrel vagy anélkül használható blokkoló eseménykezelők biztosítására. A protothreads szekvenciális vezérlést biztosít komplex állapotgépek vagy teljes többszálas menet nélkül.

Természetesen van egy Arduino példa is itt a példakóddal . Ez a SO kérdés szintén hasznos lehet.

ArduinoThread jó is.

Megjegyzések

Válasz

Az AVR alapú Arduino nem támogatja a (hardveres) szálas menetkezelést, nem ismerem az ARM alapú Arduino programot. A korlátozás egyik módja a megszakítások, különösen az időzített megszakítások használata. Programozhat egy időzítőt a fő rutin megszakítására annyi mikroszekundumban, egy adott másik rutin futtatásához.

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

Válasz

Lehetőség van szoftveroldali multi-threading végrehajtására az Uno-n. A hardverszintű szálak nem támogatottak.

A többszálas szálak eléréséhez egy alap ütemező bevezetésére és egy folyamat vagy feladatlista karbantartására van szükség a futtatni kívánt különböző feladatok nyomon követéséhez.

Egy nagyon egyszerű, nem megelőző ütemező szerkezete a következő lenne:

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

Itt, tasklist függvénymutatók tömbje lehet.

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

Az űrlap egyes funkcióival:

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

Minden funkció külön feladatot hajthat végre, például function1 LED-es manipulációkat és function2 lebegő számításokat. Minden feladat (funkció) felelőssége lesz betartani a rá szánt időt.

Remélhetőleg ennek elégnek kell lennie az induláshoz.

megjegyzések

  • nem biztos, hogy ” szálakról ” ha nem megelőző ütemezőt használ. Egyébként egy ilyen ütemező már létezik arduino könyvtárként: arduino.cc/en/Reference/Scheduler
  • @jfpoilpret – szövetkezet a többszálas valóságos dolog.
  • Igen, ‘ igazad van! Az én hibám; olyan régen még nem találkoztam kooperatív többszálú szálakkal, hogy gondolatom szerint a többszálas programnak megelőzőnek kellett lennie.

Válasz

Az Ön igényeinek leírása szerint:

  • egy szál vár egy külső eszközre
  • egy szál LED-et villog

Úgy tűnik, hogy az első “szálhoz” egy Arduino megszakítást használhat (valójában inkább “feladatnak” hívnám).

Az Arduino megszakításai egy függvényt (a kódodat) hívhatnak meg egy külső alapján esemény (feszültségszint vagy szintváltozás egy digitális bemeneti tüskén), amely azonnal elindítja a funkciót.

Azonban a megszakításoknál fontos megjegyezni, hogy a hívott funkciónak a lehető leggyorsabban kell lennie (általában nem lehet delay() hívás vagy bármilyen más API, amely a delay() függvénye lenne).

Ha hosszú feladatot kell aktiválnia a külső eseményindításkor, akkor használhat egy kooperatív ütemezőt, és új feladatot adhat hozzá a megszakítási funkcióból.

Második fontos A megszakításokkal kapcsolatos pont az, hogy korlátozott a számuk (pl. az UNO-n csak 2). Tehát, ha több külső eseményt indít el, akkor valamilyen multiplexelést kell végrehajtania az összes bemenetre egybe, és a megszakítási funkcióval meg kell határoznia, hogy a multiplexelt inut milyen tényleges kiváltó volt.

Válasz

Egyszerű megoldás egy ütemező használata . Számos megvalósítás létezik. Ez röviden leírja az AVR és SAM alapú táblák számára elérhetőt. Alapvetően egyetlen hívás indítja el a feladatot; “vázlat a vázlaton belül.” Arduino vázlat működik. A feladatnak saját vereme van. A verem mérete opcionális paraméter. Az alapértelmezett vereméret 128 bájt.

A kontextusváltás engedélyezéséhez a feladatoknak meg kell hívniuk a hozam () vagy késleltetést ( ) . Van egy támogatási makró is egy feltétel megvárására.

await(Serial.available()); 

A makró szintaktikus cukor a következőkhöz:

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

Várakozás is feladatok szinkronizálására használható. Az alábbiakban található egy példa részlet:

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); } 

További részletekért lásd az példákat . Vannak példák a többszörös villogástól a visszavonásig és egy egyszerű héjig, blokkolatlan parancssori olvasással. Sablonok és névterek használhatók a forráskód strukturálásában és csökkentésében. A vázlat alatt bemutatja, hogyan használhatja a sablonfüggvényeket a többszörös villogáshoz. Ez elég 64 bájttal a veremhez.

#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(); } 

Van egy benchmark is , amely némi képet ad a teljesítményről, azaz az időről a feladat, a kontextus kapcsoló stb. elindításához.

Végül van néhány támogatási osztály a feladat szintű szinkronizáláshoz és kommunikációhoz; Sor és szemafor .

Válasz

A mátrix LED-kijelző megvalósításakor is rátértem erre a témára.

Egy szóval , létrehozhat egy polling ütemezőt a millis () függvény és az időzítő megszakítás használatával az Arduino-ban.

Javaslom a következő cikkeket Bill Earl-től:

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

Válasz

A fórum egy korábbi igézetéből a következő kérdést / választ áthelyezték az Elektrotechnika területre. Arduino kódmintával villoghat a LED egy időzítő megszakítással, miközben a fő hurok soros IO-t használ.

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

Újra közzététel:

A megszakítások a dolgok elvégzésének általános módja, miközben valami más történik. Az alábbi példában a LED villog delay() használata nélkül. Amikor a Timer1 tüzet indít, a megszakítási szolgáltatás rutin (ISR) isrBlinker() hívásra kerül. Be- / kikapcsolja a LED-et.

Annak bemutatására, hogy más dolgok is történhetnek egyszerre, a loop() ismételten írja a foo / bar-ot a soros portra, függetlenül a LED villogásától. .

#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); } 

Ez egy nagyon egyszerű bemutató. Az ISR-ek sokkal összetettebbek lehetnek, és időzítők és külső események (csapok) válthatják ki őket. Sok közös könyvtár ISR-ek segítségével valósul meg.

Válasz

Megpróbálhatja a ThreadHandler könyvtáramat is

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

Megszakító ütemezővel engedélyezi a kontextusváltást a továbbítás nélkül hozam () vagy késleltetés ().

Azért hoztam létre a könyvtárat, mert három szálra volt szükségem, és kettőre volt szükségem ahhoz, hogy pontosan fussak, függetlenül attól, hogy mit csináltak a többiek. Az első szál a soros kommunikációt kezelte. A második egy Kalman-szűrőt futtatott, lebegő mátrix szorzást alkalmazva az Eigen könyvtárral. A harmadik pedig egy gyorsáramú vezérlőhurok-szál volt, amelynek képesnek kellett lennie a mátrixszámítások megszakítására.

Hogyan működik

Minden ciklusos szálnak van prioritása és periódusa. Ha az aktuális végrehajtó szálnál magasabb prioritású szál eléri a következő végrehajtási idejét, az ütemező szünetelteti az aktuális szálat, és áttér a magasabb prioritásúra. Amint a nagy prioritású szál befejezi a végrehajtást, az ütemező visszavált az előző szálra.

Ütemezési szabályok

A ThreadHandler könyvtár ütemezési sémája a következő:

  1. Először a legfontosabb prioritás.
  2. Ha a prioritás azonos, akkor először a legkorábbi határidővel rendelkező szál kerül végrehajtásra.
  3. Ha két szálnak ugyanaz a határideje, akkor az első létrehozott szál hajt végre először.
  4. Egy szálat csak magasabb prioritású szálak szakíthatnak meg.
  5. Amint egy szál végrehajtódik, blokkolja az összes alacsonyabb prioritású szál végrehajtását, amíg a futtatási függvény vissza nem tér.
  6. A ciklusfunkció -128 prioritással rendelkezik a ThreadHandler szálakhoz képest.

Hogyan kell használni

A szálak létrehozhatók a c ++ öröklés révén

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

Vagy a createThread és a lambda függvény segítségével

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

A szálobjektumok létrehozásakor automatikusan csatlakoznak a ThreadHandlerhez.

A létrehozott szálobjektumok végrehajtásának megkezdéséhez hívja:

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

An swer

És itt van még egy mikroprocesszoros kooperatív multitasking könyvtár – PQRST: Prioritási sor az egyszerű feladatok futtatásához.

Ebben modell, egy szál egy Task alosztályaként valósul meg, amelyet valamilyen jövőre terveznek (és esetleg rendszeres időközönként átütemeznek, ha – mint általában – a LoopTask helyett). Az objektum run() metódusát akkor hívjuk meg, amikor a feladat esedékessé válik. A run() módszer megfelelő munkát végez, majd visszatér (ez a kooperatív bit); “általában valamilyen állapotgépet tart fenn az egymást követő invokációk során végrehajtott műveleteinek kezeléséhez (triviális példa a light_on_p_ változó az alábbi példában). Enyhe újragondolást igényel rendszerezze a kódot, de nagyon rugalmasnak és robusztusnak bizonyult meglehetősen intenzív használatban.

Agnosztikus az időegységekről, tehát ugyanolyan boldog, ha millis() as micros(), vagy bármely más megfelelő pipa.

Itt található a blink program, amely ennek a könyvtárnak a segítségével valósul meg. Ez csak egyetlen futó feladatot mutat: általában más feladatokat hoznának létre, és indítanának a setup() belül.

#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()); } 

Megjegyzések

  • Ezek a „teljes körű lefutás” feladatok, igaz?
  • @EdgarBonet I ‘ nem tudom biztosan, mire gondolsz. A run() metódus meghívása után az nem szakad meg, ezért felelőssége, hogy ésszerűen azonnal befejezze. Általában azonban ‘ elvégzi a munkáját, majd átütemezi önmagát (esetleg automatikusan, LoopTask alosztály esetén) jövőbeli idő. Gyakori minta, hogy a feladat valamilyen belső állapotgépet fenntart (triviális példa a fenti állapot), hogy megfelelően viselkedjen, amikor legközelebb esedékes.
  • Tehát igen, ezek futás-befejezés (RtC) feladatok: egyetlen feladat sem futtatható, mielőtt a jelenlegi befejezné a végrehajtását a run() visszatérésével. Ez ellentétben áll a kooperatív szálakkal, amelyek például a yield() vagy az delay() hívással hozhatják a CPU-t. Vagy megelőző szálak, amelyeket bármikor ki lehet ütemezni. Fontosnak érzem a megkülönböztetést, mivel láttam, hogy sok ember, aki ide látogat szálakat keresve, ezt azért teszi, mert inkább blokkoló kódot ír, mint állami gépeket. A CPU-t eredményező valódi szálak blokkolása rendben van. Az RtC-feladatok blokkolása nem az.
  • @EdgarBonet ‘ hasznos különbség, igen. Ezt a stílust és a hozamstílusú szálakat is egyszerűen a kooperatív szál különböző stílusainak tekinteném, szemben a megelőző szálakkal, de igaz, hogy ‘ igaz, hogy más megközelítést igényelnek kódolásuk. Érdekes lenne az itt említett különféle megközelítések átgondolt és mélyreható összehasonlítását látni; az egyik szép könyvtár, amelyet nem említettünk, a protreads . Mindkettőben kritizálnivalókat találok, de dicséretet is. Én (természetesen) inkább a megközelítésemet részesítem előnyben, mert ez a legkifejezettebbnek tűnik, és nem igényel extra halmokat.
  • (javítás: a prototávokat említették , a @sachleen ‘ válasz )

Vélemény, hozzászólás?

Az email címet nem tesszük közzé. A kötelező mezőket * karakterrel jelöltük