Vraag:
Hoe kan ik meerdere lopende threads maken?
Bja
2014-02-18 21:11:42 UTC
view on stackexchange narkive permalink

Is er een manier waarop ik meerdere delen van het programma samen kan laten draaien zonder meerdere dingen in hetzelfde codeblok te doen?

Een thread wacht op een extern apparaat terwijl ook een LED in een andere thread knippert.

U moet zich waarschijnlijk eerst afvragen of u draden echt nodig heeft. Timers zijn misschien al in orde voor uw behoeften en ze worden standaard ondersteund op Arduino.
Misschien wil je ook de Uzebox eens bekijken. Het is een homebrew videogameconsole met twee chips. Dus hoewel het niet echt een Arduino is, is het hele systeem gebouwd op interrupts. Dus audio, video, bedieningselementen, enz. Worden allemaal onderbroken, terwijl het hoofdprogramma zich er geen zorgen over hoeft te maken. Misschien een goede referentie.
Negen antwoorden:
#1
+54
sachleen
2014-02-18 23:23:18 UTC
view on stackexchange narkive permalink

Er is geen ondersteuning voor meerdere processen of meerdere threads op de Arduino. Je kunt met sommige software echter iets doen dat in de buurt komt van meerdere threads.

Je wilt protothreads bekijken:

Protothreads zijn extreem lichtgewicht stapelloze threads ontworpen voor systemen met een ernstig geheugenbeperking, zoals kleine embedded systemen of draadloze sensornetwerkknooppunten. Protothreads bieden lineaire code-uitvoering voor gebeurtenisgestuurde systemen die zijn geïmplementeerd in C. Protothreads kunnen met of zonder een onderliggend besturingssysteem worden gebruikt om gebeurtenisafhandelaars te blokkeren. Protothreads bieden een sequentiële controlestroom zonder complexe toestandsmachines of volledige multi-threading.

Natuurlijk is er hier een Arduino-voorbeeld met voorbeeldcode. Deze SO-vraag kan ook nuttig zijn.

ArduinoThread is ook een goede.

Merk op dat de Arduino DUE hierop een uitzondering heeft, met meerdere controlelussen: https://www.arduino.cc/en/Tutorial/MultipleBlinks
#2
+19
jippie
2014-02-19 00:14:37 UTC
view on stackexchange narkive permalink

AVR gebaseerde Arduino's ondersteunen geen (hardware) threading, ik ben niet bekend met de ARM gebaseerde Arduino's. Een manier om deze beperking te omzeilen, is het gebruik van interrupts, vooral getimede interrupts. Je kunt een timer programmeren om de hoofdroutine om de zoveel microseconden te onderbreken, om een ​​specifieke andere routine uit te voeren.

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

#3
+15
asheeshr
2014-02-18 21:36:33 UTC
view on stackexchange narkive permalink

Het is mogelijk om software-side multi-threading uit te voeren op de Uno. Threading op hardwareniveau wordt niet ondersteund.

Om multithreading te bereiken, is de implementatie van een basisplanner vereist en het bijhouden van een proces of takenlijst om de verschillende taken bij te houden die moeten worden uitgevoerd.

De structuur van een zeer eenvoudige niet-preventieve planner zou er als volgt uitzien:

  // Pseudocodevoid loop () {voor (i = o; i<n; i ++) run ( takenlijst [i] voor tijdslimiet):}  

Hier kan takenlijst een reeks functie-aanwijzers zijn.

  takenlijst [ ] = {functie1, functie2, functie3, ...}  

Met elke functie van de vorm:

  int functie1 (lange tijd_beschikbaar) {top: // Voer een korte taak uit als (run_time<time_available) goto top;}  

Elke functie kan een afzonderlijke taak uitvoeren, zoals function1 LED-manipulaties uitvoeren, en function2 doet float-berekeningen. Het is de verantwoordelijkheid van elke taak (functie) om zich te houden aan de tijd die eraan is toegewezen.

Hopelijk zou dit voldoende moeten zijn om u op weg te helpen.

Ik weet niet zeker of ik het zou hebben over "threads" als ik een niet-preventieve planner gebruik. Overigens bestaat zo'n planner al als een Arduino-bibliotheek: http://arduino.cc/en/Reference/Scheduler
@jfpoilpret - Coöperatieve multithreading is echt.
Ja je hebt gelijk! Mijn fout; het was zo lang geleden dat ik nog niet met coöperatieve multithreading te maken had gehad dat in mijn gedachten multithreading preventief moest zijn.
#4
+9
jfpoilpret
2014-02-19 02:58:56 UTC
view on stackexchange narkive permalink

Volgens de beschrijving van uw vereisten:

  • één thread wachtend op een extern apparaat
  • één thread knippert een LED

Het lijkt erop dat je één Arduino-interrupt zou kunnen gebruiken voor de eerste "thread" (ik zou het eigenlijk liever "task" noemen).

Arduino-interrupts kunnen één functie (jouw code) oproepen op basis van een externe gebeurtenis (spanningsniveau of niveauverandering op een digitale ingangspen), dat zal uw functie onmiddellijk activeren.

Een belangrijk punt om in gedachten te houden met interrupts is dat de aangeroepen functie zo snel mogelijk moet zijn ( normaal gesproken zou er geen delay () aanroep of een andere API moeten zijn die afhankelijk is van delay()).

Als je een lange taak hebt om te activeren bij trigger van externe gebeurtenissen, dan zou je mogelijk een coöperatieve planner kunnen gebruiken en er een nieuwe taak aan toevoegen vanuit je interrupt-functie.

Een tweede belangrijk punt over interrupts is dat hun aantal beperkt is (bijvoorbeeld slechts 2 op UNO). Dus als je meer externe gebeurtenissen begint te krijgen, zou je een soort van multiplexing van alle ingangen in één moeten implementeren, en je interruptfunctie laten bepalen welke gemultiplexte ingang de feitelijke trigger was.

#5
+7
Mikael Patel
2016-01-21 18:34:10 UTC
view on stackexchange narkive permalink

Een eenvoudige oplossing is om een ​​ Scheduler te gebruiken. Er zijn verschillende implementaties. Dit beschrijft binnenkort een die beschikbaar is voor op AVR en SAM gebaseerde kaarten. In principe start een enkele oproep een taak; "schets binnen een schets".

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

Scheduler.start () zal een nieuwe taak toevoegen die de taskSetup één keer zal uitvoeren en vervolgens herhaaldelijk taskLoop zal aanroepen, net zoals de Arduino-sketch werkt. De taak heeft zijn eigen stapel. De grootte van de stapel is een optionele parameter. De standaard stackgrootte is 128 bytes.

Om contextwisseling mogelijk te maken, moeten de taken yield () of delay () aanroepen. Er is ook een ondersteuningsmacro voor het wachten op een conditie.

await(Serial.available());

De macro is syntactisch voor het volgende:

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

Await kan ook worden gebruikt om taken te synchroniseren. Hieronder is een voorbeeldfragment:

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

Zie voor meer details de voorbeelden. Er zijn voorbeelden van meerdere LED-knipper- tot debounce-knop en een eenvoudige shell met niet-blokkerende opdrachtregel om te lezen. Sjablonen en naamruimten kunnen worden gebruikt om de broncode te structureren en te verkleinen. Onderstaande schets laat zien hoe u sjabloonfuncties gebruikt voor meervoudig knipperen. Het is voldoende met 64 bytes voor de stapel.

  #include <Scheduler.h>template<int pin> void setupBlink () {pinMode (pin, OUTPUT);} template<int pin, niet-ondertekende int ms<int vertraging (ms); digitalWrite (pin, LOW); vertraging (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 ();}  

Er is ook een benchmark om wat idee van de prestatie, dwz tijd om de taak te starten, contextwisseling, enz.

Ten slotte zijn er een paar ondersteuningsklassen voor synchronisatie en communicatie op taakniveau; wachtrij en semafoor.

#6
+3
walrii
2015-09-04 02:57:26 UTC
view on stackexchange narkive permalink

Vanuit een eerdere bezwering van dit forum is de volgende vraag / antwoord verplaatst naar Electrical Engineering. Het heeft een voorbeeld van Arduino-code om een ​​LED te laten knipperen met behulp van een timeronderbreking terwijl de hoofdlus wordt gebruikt om seriële IO uit te voeren.

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

Repost:

Onderbrekingen zijn een gebruikelijke manier om dingen gedaan te krijgen terwijl er iets anders aan de hand is Aan. In het onderstaande voorbeeld knippert de LED zonder gebruik te maken van delay () . Telkens wanneer Timer1 wordt geactiveerd, wordt de interruptserviceroutine (ISR) isrBlinker () aangeroepen. Het schakelt de LED aan / uit.

Om te laten zien dat er andere dingen tegelijkertijd kunnen gebeuren, schrijft loop () herhaaldelijk foo / bar naar de seriële poort, onafhankelijk van het knipperen van de LED.

  #include "TimerOne.h" int led = 13; void isrBlinker () {static bool on = false; digitalWrite (led, aan? HIGH: LOW); on =! on;} void setup () {Serial.begin (9600); Serial.flush (); Serial.println ("Serial geïnitialiseerd"); pinMode (led, OUTPUT); // initialiseer de ISR-blinker Timer1.initialize (1000000); Timer1.attachInterrupt (isrBlinker);} void loop () {Serial.println ("foo"); vertraging (1000); Serial.println ("balk"); delay (1000);}  

Dit is een heel eenvoudige demo. ISR's kunnen veel complexer zijn en kunnen worden geactiveerd door timers en externe gebeurtenissen (pinnen). Veel van de gebruikelijke bibliotheken worden geïmplementeerd met behulp van ISR's.

#7
+3
intelliarm
2015-09-04 14:22:56 UTC
view on stackexchange narkive permalink

Ik kwam ook op dit onderwerp toen ik een matrix LED-display implementeerde.

In één woord, je kunt een polling-planner bouwen door de millis () -functie en timer-interrupt in Arduino te gebruiken.

Ik stel de volgende artikelen van Bill Earl voor:

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

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

https: // leren .adafruit.com / multi-tasking-the-arduino-part-3 / overzicht

#8
+2
Adam Bäckström
2019-04-22 15:59:03 UTC
view on stackexchange narkive permalink

Je zou mijn ThreadHandler-bibliotheek ook eens kunnen proberen

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

gebruikt een onderbrekende planner om contextwisseling mogelijk te maken zonder door te geven aan yield () of delay ().

Ik heb de bibliotheek gemaakt omdat ik drie threads nodig had en ik had er twee nodig om op een precies tijdstip te draaien, wat er ook gebeurde de anderen deden het. De eerste thread behandelde seriële communicatie. De tweede was het uitvoeren van een Kalman-filter met behulp van float-matrixvermenigvuldiging met de Eigen-bibliotheek. En de derde was een snelle stroomregellus-thread die de matrixberekeningen moest kunnen onderbreken.

Hoe het werkt

Elke cyclische thread heeft een prioriteit en een periode. Als een thread, met een hogere prioriteit dan de huidige uitvoerende thread, de volgende uitvoeringstijd bereikt, zal de planner de huidige thread pauzeren en overschakelen naar de thread met een hogere prioriteit. Zodra de thread met hoge prioriteit zijn uitvoering heeft voltooid, schakelt de planner terug naar de vorige thread.

Planningsregels

Het planningsschema van de ThreadHandler-bibliotheek is als volgt:

  1. Eerst de hoogste prioriteit.
  2. Als de prioriteit hetzelfde is, wordt de thread met de vroegste deadline als eerste uitgevoerd.
  3. Als twee threads dezelfde deadline hebben, wordt de eerste gemaakt thread zal als eerste worden uitgevoerd.
  4. Een thread kan alleen worden onderbroken door threads met een hogere prioriteit.
  5. Zodra een thread wordt uitgevoerd, wordt de uitvoering voor alle threads met lagere prioriteit geblokkeerd tot de run-functie geeft terug.
  6. De loop-functie heeft prioriteit -128 in vergelijking met ThreadHandler-threads.

Hoe te gebruiken

Threads kunnen worden gemaakt via c ++ overerving

  class MyThread: public Thread {public: MyThread (): Thread (prioriteit, punt, offset) {} virtual ~ MyThread () {} virtuele leegte run () {// code om uit te voeren }}; MyThread * threadObj = nieuwe MyThread ();  

Of via createThread en een lambda-functie

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

Thread-objecten maken automatisch verbinding met de ThreadHandler wanneer ze worden gemaakt.

Om de uitvoering van gemaakte thread-objecten te starten, roept u:

  ThreadHandler :: getInstance () - >enableThreadExecution ();  
#9
+1
Norman Gray
2019-07-08 20:41:54 UTC
view on stackexchange narkive permalink

En hier is nog een andere coöperatieve multitaskingbibliotheek met microprocessors - PQRST: een prioriteitswachtrij voor het uitvoeren van eenvoudige taken.

In dit model wordt een thread geïmplementeerd als een subklasse van een Taak , die is gepland voor een toekomstige tijd (en mogelijk met regelmatige tussenpozen opnieuw wordt gepland, als het, zoals gebruikelijk, subklassen LoopTask in plaats daarvan). De run () -methode van het object wordt aangeroepen wanneer de taak vervalt. De run () methode doet wat gepast werk, en keert dan terug (dit is het coöperatieve bit); het zal typisch een soort toestandsmachine onderhouden om zijn acties op opeenvolgende aanroepen te beheren (een triviaal voorbeeld is de variabele light_on_p_ in het onderstaande voorbeeld). Het vereist een kleine heroverweging van hoe je je code organiseert, maar het is erg flexibel en robuust gebleken bij redelijk intensief gebruik.

Het is agnostisch over de tijdseenheden, dus het is net zo gelukkig om in eenheden van te rennen. millis () als micros () , of een ander vinkje dat handig is.

Hier is het 'blink'-programma geïmplementeerd met behulp van deze bibliotheek. Dit laat zien dat slechts een enkele taak wordt uitgevoerd: andere taken worden normaal gesproken gemaakt en gestart met setup().

  #include "pqrst.h" class BlinkTask: public LoopTask {privé: int my_pin_; bool light_on_p_; public: BlinkTask (int pin, ms_t cadans); 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) { // schakel de LED-status elke keer dat we light_on_p_ =! light_on_p_ worden genoemd; digitalWrite (my_pin_, light_on_p _);} // flash de ingebouwde LED met een 500ms cadenceBlinkTask flasher (LED_BUILTIN, 500); void setup () {pinMode (LED_BUILTIN, OUTPUT); flasher.start (2000); // start na 2000ms (= 2s)}
void loop () {Queue.run_ready (millis ());}  
Dit zijn "run-to-complete" -taken, toch?
@EdgarBonet Ik weet niet precies wat je bedoelt. Nadat de `run ()` methode is aangeroepen, wordt deze niet onderbroken, dus het heeft de verantwoordelijkheid om redelijk snel te eindigen. Meestal zal het echter zijn werk doen en zichzelf opnieuw plannen (mogelijk automatisch, in het geval van een subklasse van `LoopTask`) voor een toekomstige tijd. Een veelvoorkomend patroon is dat de taak een interne toestandsmachine onderhoudt (een triviaal voorbeeld is de `light_on_p_` toestand hierboven), zodat deze zich op de juiste manier gedraagt ​​wanneer deze de volgende keer moet plaatsvinden.
Dus ja, dat zijn run-to-completing (RtC) -taken: geen enkele taak kan worden uitgevoerd voordat de huidige is voltooid door terug te keren van `run ()`. Dit in tegenstelling tot coöperatieve threads, die de CPU kunnen opleveren door bijvoorbeeld `yield ()` of `delay ()` aan te roepen. Of preventieve threads, die op elk moment kunnen worden gepland. Ik denk dat het onderscheid belangrijk is, omdat ik heb gezien dat veel mensen die hier naar threads zoeken, dit doen omdat ze liever blokkeercode schrijven dan toestandsmachines. Het blokkeren van echte threads die de CPU opleveren, is prima. Het blokkeren van RtC-taken is dat niet.
@EdgarBonet Het is een nuttig onderscheid, ja. Ik zou zowel deze stijl als de yield-stijl threads beschouwen als eenvoudig verschillende stijlen van coöperatieve threads, in tegenstelling tot preventieve threads, maar het is waar dat ze een andere benadering nodig hebben om ze te coderen. Het zou interessant zijn om een ​​doordachte en diepgaande vergelijking te zien van de verschillende hier genoemde benaderingen; een leuke bibliotheek die hierboven niet genoemd is, is [protothreads] (http://dunkels.com/adam/pt/). In beide vind ik dingen om te bekritiseren, maar ook om te prijzen. Ik geef (natuurlijk) de voorkeur aan mijn aanpak, omdat deze het meest expliciet lijkt en geen extra stapels nodig heeft.
(correctie: protothreads _was_ vermeld, in [@sachleen's antwoord] (https://arduino.stackexchange.com/a/288/8391))


Deze Q&A is automatisch vertaald vanuit de Engelse taal.De originele inhoud is beschikbaar op stackexchange, waarvoor we bedanken voor de cc by-sa 3.0-licentie waaronder het wordt gedistribueerd.
Loading...