Hvordan kan jeg oprette flere kørende tråde?

Er der en måde, hvorpå jeg kan have flere dele af programmet kørende sammen uden at gøre flere ting i den samme kodeblok?

En tråd venter på en ekstern enhed, mens du også blinker en LED i en anden tråd.

Kommentarer

  • Du bør sandsynligvis først spørge dig selv, om du virkelig har brug for tråde. Timere kan muligvis allerede være i orden til dine behov, og de understøttes oprindeligt på Arduino.
  • Det kan også være en god idé at tjekke Uzebox. Det ‘ er en tochips hjemmebrygget videospilkonsol. Så selvom det ikke er ‘ t nøjagtigt en Arduino, er hele systemet bygget på afbrydelser. Så lyd, video, kontrol osv. Er alle afbrydelsesdrevne, mens hovedprogrammet ikke ‘ behøver at bekymre sig om noget af det. Kan være en god reference.

Svar

Der er ingen understøttelse af flere processer eller multi-threading Arduino. Du kan dog gøre noget tæt på flere tråde med noget software.

Du vil se på Protothreads :

Protothreads er ekstremt lette stakløse tråde designet til stærkt hukommelsesbegrænsede systemer, såsom små indlejrede systemer eller trådløse sensornetværksnoder. Protothreads giver lineær kodeudførelse til hændelsesdrevne systemer implementeret i C. Protothreads kan bruges med eller uden et underliggende operativsystem til at tilvejebringe blokerende hændelseshåndterere. Protothreads giver sekventiel kontrol af kontrol uden komplekse tilstandsmaskiner eller fuld multitrådning.

Der er naturligvis et Arduino-eksempel her med eksempelkode . Dette SO-spørgsmål kan også være nyttigt.

ArduinoThread er også en god.

Kommentarer

Svar

AVR-baserede Arduino “understøtter ikke (hardware) threading, jeg er ikke bekendt med de ARM-baserede Arduino” s. En vej rundt denne begrænsning er brugen af afbrydelser, især afbrudte tider. Du kan programmere en timer til at afbryde hovedrutinen hver så mange mikrosekunder for at køre en bestemt anden rutine.

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

Svar

Det er muligt at lave softwareside multitrådning på Uno. Trådning af hardwareniveau understøttes ikke.

For at opnå multithreading kræves det implementering af en grundlæggende planlægning og vedligeholdelse af en proces eller opgaveliste for at spore de forskellige opgaver, der skal køres.

Strukturen i en meget enkel ikke-forebyggende planlægning vil være som:

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

Her, tasklist kan være en række funktionsmarkører.

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

Med hver funktion i formen:

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

Hver funktion kan udføre en separat opgave såsom function1 udføre LED-manipulationer og function2 udføre floatberegninger. Det er hver opgave (funktion )s ansvar at overholde den tid, der er afsat til den.

Forhåbentlig skulle dette være nok til at komme i gang.

Kommentarer

  • Jeg er ikke sikker på, at jeg vil tale om ” tråde ” når du bruger en ikke-forebyggende planlægning. Forresten eksisterer en sådan planlægning allerede som et arduino-bibliotek: arduino.cc/da/Reference/Scheduler
  • @jfpoilpret – Kooperativ multithreading er en rigtig ting.
  • Ja du ‘ har ret! Min fejl; det havde været så længe siden, at jeg ikke stod over for kooperativ multithreading, at multithreading i mine øjne måtte være præventiv.

Svar

Som beskrevet i beskrivelsen af dine krav:

  • en tråd venter på en ekstern enhed
  • en tråd blinker en LED

Det ser ud til, at du kunne bruge en Arduino-afbrydelse til den første “tråd” (jeg vil hellere kalde det “opgave”).

Arduino-afbrydelser kan kalde en funktion (din kode) baseret på en ekstern begivenhed (spændingsniveau eller niveauændring på en digital indgangsstift), der vil udløse din funktion med det samme.

Et vigtigt punkt at huske på ved afbrydelser er imidlertid, at den kaldte funktion skal være så hurtig som muligt (typisk bør der ikke være noget delay() -opkald eller noget andet API, der afhænger af delay()).

Hvis du har en lang opgave at aktivere ved udløsning af ekstern begivenhed, kan du muligvis bruge en samarbejdsplanlægger og tilføje en ny opgave til den fra din afbrydelsesfunktion.

Et andet vigtigt pointen om afbrydelser er, at antallet er begrænset (f.eks. kun 2 på UNO). Så hvis du begynder at have flere eksterne begivenheder, skal du implementere en slags multiplexing af alle input til en og få din afbrydelsesfunktion til at bestemme, hvilken multiplexed inut der var den egentlige trigger.

Svar

En enkel løsning er at bruge en Planlægger . Der er flere implementeringer. Dette beskriver kort en, der er tilgængelig for AVR- og SAM-baserede kort. Grundlæggende starter et enkelt opkald en opgave; “sketch in a sketch”.

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

Scheduler.start () tilføjer en ny opgave, der kører taskSetup en gang og derefter gentagne gange kalder taskLoop lige som Arduino skitseværker. Opgaven har sin egen stak. Stakkens størrelse er en valgfri parameter. Standardstørrelsen på stakken er 128 byte.

For at tillade kontekstskift skal opgaverne ringe til yield () eller forsinkelse ( ) . Der er også en supportmakro til at vente på en tilstand.

await(Serial.available()); 

Makroen er syntaktisk sukker for følgende:

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

Afventer kan også bruges til at synkronisere opgaver. Nedenfor er et eksempel på et uddrag:

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

For yderligere oplysninger se eksempler . Der er eksempler fra flere LED-blink for at afvise-knappen og en simpel skal med ikke-blokerende kommandolinje læst. Skabeloner og navneområder kan bruges til at strukturere og reducere kildekoden. Nedenfor skitse vises, hvordan man bruger skabelonfunktioner til multi-blink. Det er tilstrækkeligt med 64 byte til stakken.

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

Der er også en benchmark for at give en ide om forestillingen, dvs. tid at starte opgave, kontekstskifte osv.

Endelig er der et par supportklasser til synkronisering og kommunikation på jobniveau; og Semaphore .

Svar

Jeg kom også til dette emne, mens jeg implementerede en matrix-LED-skærm.

I et ord , kan du oprette en afstemningsplanlægning ved hjælp af millis () -funktionen og timer-afbrydelse i Arduino.

Jeg foreslår følgende artikler fra Bill Earl:

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

Svar

Fra en tidligere besværgelse af dette forum blev følgende spørgsmål / svar flyttet til elektroteknik. Det har en arduino-kode til at blinke en LED ved hjælp af en timer-afbrydelse, mens du bruger hovedsløjfen til at udføre seriel IO.

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

Repost:

Interrupts er en almindelig måde at få tingene til, mens noget andet foregår. I eksemplet nedenfor blinker LEDen uden at bruge delay(). Når Timer1 udløses, kaldes afbrydelsesrutinen (ISR) isrBlinker(). Det tænder / slukker for lysdioden.

For at vise, at andre ting kan ske samtidigt, loop() skriver gentagne gange foo / bar til den serielle port uafhængig af, at LED blinker .

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

Dette er en meget enkel demo. ISRer kan være meget mere komplekse og kan udløses af timere og eksterne begivenheder (pins). Mange af de almindelige biblioteker implementeres ved hjælp af ISRer.

Svar

Du kan også prøve mit ThreadHandler-bibliotek

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

Det bruger en afbrydende planlægning til at tillade kontekstskift uden at videresende yield () eller delay ().

Jeg oprettede biblioteket, fordi jeg havde brug for tre tråde, og jeg havde brug for to af dem til at køre på et præcist tidspunkt, uanset hvad de andre gjorde. Den første trådhåndterede seriel kommunikation. Den anden kørte et Kalman-filter ved hjælp af floatmatrix-multiplikation med Eigen-biblioteket. Og den tredje var en hurtigstrømstyringsløbetråd, som skulle kunne afbryde matrixberegningerne.

Sådan fungerer det

Hver cyklisk tråd har en prioritet og en periode. Hvis en tråd, der har højere prioritet end den aktuelle udførende tråd, når sin næste udførelsestid, planlægger planlæggeren den aktuelle tråd på pause og skifter til den højere prioritet. Når tråden med høj prioritet er færdig med sin udførelse, skifter planlæggeren tilbage til den forrige tråd.

Planlægningsregler

Planlægningsskemaet for ThreadHandler-biblioteket er som følger:

  1. Højeste prioritet først.
  2. Hvis prioriteten er den samme, udføres tråden med den tidligste deadline først.
  3. Hvis to tråde har den samme deadline, udføres den først oprettede tråd først.
  4. En tråd kan kun afbrydes af tråde med højere prioritet.
  5. Når en tråd er udført, blokerer den udførelse for alle tråde med lavere prioritet, indtil kørselsfunktionen vender tilbage.
  6. Loop-funktionen har prioritet -128 sammenlignet med ThreadHandler-tråde.

Sådan bruges

Tråde kan oprettes via c ++ arv

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

Eller via createThread og en lambda-funktion

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

Trådobjekter opretter automatisk forbindelse til ThreadHandler, når de oprettes.

For at starte udførelsen af oprettede trådobjekter, ring:

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

En svare

Og her er endnu et mikroprocessor-kooperativt multitasking-bibliotek – PQRST: en prioritetskø til kørsel af enkle opgaver.

I denne model, er en tråd implementeret som en underklasse af en Task, som er planlagt til et fremtidigt tidspunkt (og muligvis omlagt med regelmæssige intervaller, hvis den, som det er almindeligt, underklasser LoopTask i stedet). run() -metoden for objektet kaldes, når opgaven forfalder. run() -metoden gør noget arbejde, og returnerer derefter (dette er den kooperative bit); det “opretholder typisk en slags statsmaskine til at styre sine handlinger på successive påkaldelser (et trivielt eksempel er light_on_p_ -variablen i nedenstående eksempel). Det kræver en lille overvejelse af, hvordan organisere din kode, men har vist sig at være meget fleksibel og robust i forholdsvis intensiv brug.

Det er agnostisk om tidsenhederne, så det er lige så lykkeligt at køre i enheder på millis() som micros() eller ethvert andet kryds, der er praktisk.

Her er blink-programmet implementeret ved hjælp af dette bibliotek. Dette viser kun en enkelt opgave, der kører: andre opgaver oprettes typisk og startes inden for 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()); } 

Kommentarer

  • Dette er “run-to-complete” opgaver, ikke?
  • @EdgarBonet I ‘ Jeg er ikke helt sikker på, hvad du mener. Når run() -metoden er kaldt, afbrydes den ikke, så den har ansvaret for at afslutte med rimelighed straks. Typisk dog ‘ vil udføre sit arbejde og derefter omlægge sig selv (muligvis automatisk i tilfælde af en underklasse af LoopTask) for nogle fremtidig tid. Et almindeligt mønster er, at opgaven skal opretholde en intern tilstandsmaskine (et trivielt eksempel er light_on_p_ -tilstanden ovenfor), så den opfører sig passende, når den næste gang forfalder.
  • Så ja, det er RtC-opgaver (run-to-complete): ingen opgave kan køre, før den nuværende fuldfører sin udførelse ved at vende tilbage fra run(). Dette er i modsætning til kooperative tråde, der kan give CPUen ved f.eks. At ringe til yield() eller delay(). Eller præemptive tråde, der kan planlægges ud når som helst. Jeg føler forskellen er vigtig, da jeg har set, at mange mennesker, der kommer rundt her og søger efter tråde, gør det, fordi de foretrækker at skrive blokeringskode frem for statsmaskiner. At blokere ægte tråde, der giver CPUen, er fint. At blokere RtC-opgaver er ikke.
  • @EdgarBonet Det ‘ er en nyttig skelnen, ja. Jeg vil betragte både denne stil og tråde med udbyttestil som ganske enkelt forskellige stilarter af kooperativ tråd i modsætning til præemptive tråde, men det ‘ er sandt, at de kræver en anden tilgang til kodning af dem. Det ville være interessant at se en tankevækkende og dybtgående sammenligning af de forskellige tilgange, der er nævnt her; et godt bibliotek, der ikke er nævnt ovenfor, er protothreads . Jeg finder ting at kritisere i begge dele, men også at rose. Jeg foretrækker (selvfølgelig) min tilgang, fordi den virker mest eksplicit og ikke behøver ekstra stakke.
  • (rettelse: prototråde blev nævnt i @sachleen ‘ s svar )

Skriv et svar

Din e-mailadresse vil ikke blive publiceret. Krævede felter er markeret med *