मैं कई रनिंग थ्रेड्स कैसे बना सकता हूं?


60

क्या एक तरह से मैं प्रोग्राम के कई हिस्सों को एक ही कोड ब्लॉक में कई चीजें किए बिना एक साथ चल सकता हूं?

एक थ्रेड बाहरी डिवाइस के लिए इंतजार कर रहा है, जबकि दूसरे थ्रेड में एक एलईडी ब्लिंक कर रहा है।


3
आपको शायद पहले खुद से पूछना चाहिए कि क्या आपको वास्तव में थ्रेड्स की आवश्यकता है। टाइमर आपकी आवश्यकताओं के लिए पहले से ही ठीक हो सकते हैं और वे मूल रूप से Arduino पर समर्थित हैं।
jfpoilpret

1
आप उज़ेबॉक्स की भी जाँच कर सकते हैं। यह एक दो चिप homebrew वीडियो गेम कंसोल है। जब तक कि यह वास्तव में एक Arduino नहीं है, तब तक पूरा सिस्टम इंटरप्ट पर बनाया गया है। इसलिए ऑडियो, वीडियो, नियंत्रण आदि सभी बाधित हैं, जबकि मुख्य कार्यक्रम को इसके बारे में चिंता करने की आवश्यकता नहीं है। अच्छा संदर्भ हो सकता है।
cbmeeks

जवाबों:


50

Arduino पर कोई बहु-प्रक्रिया, और न ही बहु-थ्रेडिंग, समर्थन है। आप कुछ सॉफ्टवेयर के साथ कई थ्रेड के करीब कुछ कर सकते हैं।

आप प्रोटोथ्रेड्स को देखना चाहते हैं :

प्रोटॉथ्रेड्स बेहद हल्के स्टैकलेस थ्रेड्स हैं जो गंभीर मेमोरी की कमी वाले सिस्टम के लिए डिज़ाइन किए गए हैं, जैसे कि छोटे एम्बेडेड सिस्टम या वायरलेस सेंसर नेटवर्क नोड्स। प्रोटोथ्रेड सी में कार्यान्वित इवेंट-संचालित सिस्टम के लिए रैखिक कोड निष्पादन प्रदान करते हैं। ब्लॉक-हैंडलर को प्रदान करने के लिए एक अंतर्निहित ऑपरेटिंग सिस्टम के साथ या बिना प्रोटॉथ्रेड्स का उपयोग किया जा सकता है। प्रोटॉथ्रेड्स जटिल राज्य मशीनों या पूर्ण बहु-थ्रेडिंग के बिना नियंत्रण के अनुक्रमिक प्रवाह प्रदान करते हैं।

बेशक, यहाँ एक Arduino उदाहरण उदाहरण कोड के साथ है । यह SO प्रश्न उपयोगी भी हो सकता है।

ArduinoThread एक अच्छा भी है।


ध्यान दें कि Arduino के कारण कई नियंत्रण छोरों के साथ, इस के लिए एक अपवाद है: arduino.cc/en/Tutorial/MultipleBlinks
tuskiomi

18

AVR आधारित Arduino का समर्थन नहीं करता है (हार्डवेयर) सूत्रण, मैं एआरएम आधारित Arduino के साथ अपरिचित हूं। इस सीमा के चारों ओर एक तरह से व्यवधानों का उपयोग होता है, विशेषकर समयबद्ध व्यवधानों का। आप किसी विशिष्ट अन्य दिनचर्या को चलाने के लिए हर इतने माइक्रोसेकंड में मुख्य दिनचर्या को बाधित करने के लिए एक टाइमर कार्यक्रम कर सकते हैं।

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


15

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एलईडी जोड़तोड़ प्रदर्शन करना, और function2फ्लोट गणना करना। इसे आवंटित समय का पालन करना प्रत्येक कार्य (कार्य) की जिम्मेदारी होगी।

उम्मीद है, यह आपको शुरू करने के लिए पर्याप्त होना चाहिए।


2
मुझे यकीन नहीं है कि मैं गैर-पूर्वनिर्धारित अनुसूचक का उपयोग करते समय "थ्रेड्स" के बारे में बात करूंगा। वैसे, इस तरह के एक अनुसूचक पहले से ही एक Arduino पुस्तकालय के रूप में मौजूद है: arduino.cc/en/Reference/Scheduler
jfpoilpret

5
@jfpoilpret - सहकारिता मल्टीथ्रेडिंग एक वास्तविक चीज है।
कॉनर वुल्फ

हाँ तुम सही हो! मेरी गलती; ऐसा बहुत पहले हो चुका था जब मैंने सहकारी बहु-प्रसार का सामना नहीं किया था कि मेरे दिमाग में, मल्टीथ्रेडिंग को प्रीमिटिव होना चाहिए था।
jfpoilpret

9

अपनी आवश्यकताओं के विवरण के अनुसार:

  • एक धागा एक बाहरी उपकरण की प्रतीक्षा कर रहा है
  • एक एलईडी निमिष एक धागा

ऐसा लगता है कि आप पहले "थ्रेड" के लिए एक Arduino इंटरप्ट का उपयोग कर सकते हैं (मैं वास्तव में इसे "कार्य" कहूंगा)।

Arduino इंटरप्ट एक बाहरी घटना (एक डिजिटल इनपुट पिन पर वोल्टेज स्तर या स्तर परिवर्तन) के आधार पर एक फ़ंक्शन (आपके कोड) को कॉल कर सकता है, जो आपके फ़ंक्शन को तुरंत ट्रिगर करेगा।

हालांकि, इंटरप्ट के साथ ध्यान में रखने के लिए एक महत्वपूर्ण बिंदु यह है कि बुलाया फ़ंक्शन जितना संभव हो उतना तेज़ होना चाहिए (आमतौर पर, कोई delay()कॉल या कोई अन्य एपीआई नहीं होना चाहिए जो उस पर निर्भर हो delay())।

यदि आपके पास बाहरी ईवेंट ट्रिगर पर सक्रिय करने के लिए एक लंबा कार्य है, तो आप संभावित रूप से एक सहकारी अनुसूचक का उपयोग कर सकते हैं और अपने रुकावट फ़ंक्शन से इसमें एक नया कार्य जोड़ सकते हैं।

इंटरप्ट के बारे में एक दूसरा महत्वपूर्ण बिंदु यह है कि उनकी संख्या सीमित है (उदाहरण के लिए संयुक्त राष्ट्र पर केवल 2)। इसलिए यदि आप अधिक बाहरी घटनाओं को अंजाम देना शुरू करते हैं, तो आपको सभी इनपुटों को किसी एक प्रकार के मल्टीप्लेक्सिंग को लागू करने की आवश्यकता होगी, और आपके रुकावट फ़ंक्शन को यह निर्धारित करना होगा कि मल्टीप्लेक्स इनट क्या वास्तविक ट्रिगर था।


6

एक सरल समाधान एक समयबद्धक का उपयोग करना है । कई कार्यान्वयन हैं। यह शीघ्र ही एवीआर और एसएएम आधारित बोर्डों के लिए उपलब्ध है। मूल रूप से एक एकल कॉल एक कार्य शुरू करेगा; "स्केच के भीतर स्केच"।

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

शेड्यूलर.स्टार्ट () एक नया कार्य जोड़ देगा जो एक बार टास्कसेट को चलाएगा और फिर बार-बार टास्कलॉप को कॉल करेगा, जैसे कि Arduino स्केच काम करता है। कार्य का अपना एक स्टैक है। स्टैक का आकार एक वैकल्पिक पैरामीटर है। डिफ़ॉल्ट स्टैक का आकार 128 बाइट्स है।

संदर्भ स्विच करने के लिए कार्यों को उपज () या देरी () को कॉल करने की आवश्यकता है । किसी शर्त का इंतजार करने के लिए एक सपोर्ट मैक्रो भी है।

await(Serial.available());

मैक्रो निम्नलिखित के लिए सिंटैक्टिक चीनी है:

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

Await का उपयोग कार्यों को सिंक्रनाइज़ करने के लिए भी किया जा सकता है। नीचे एक उदाहरण स्निपेट है:

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

अधिक जानकारी के लिए उदाहरण देखें । मल्टीपल एलईडी ब्लिंक से लेकर डेब्यू बटन तक के उदाहरण और नॉन-ब्लॉकिंग कमांड लाइन के साथ एक साधारण शेल है। स्रोत कोड को कम करने और कम करने के लिए टेम्पलेट और नामस्थान का उपयोग किया जा सकता है। नीचे स्केच से पता चलता है कि मल्टी-ब्लिंक के लिए टेम्पलेट फ़ंक्शन का उपयोग कैसे करें। यह स्टैक के लिए 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();
}

प्रदर्शन का कुछ विचार देने के लिए एक बेंचमार्क भी है , अर्थात् कार्य शुरू करने के लिए समय, संदर्भ स्विच, आदि।

अंतिम, कार्य स्तर सिंक्रनाइज़ेशन और संचार के लिए कुछ सहायक कक्षाएं हैं; कतार और सेमाफोर


3

इस मंच के पिछले झुकाव से, निम्नलिखित प्रश्न / उत्तर इलेक्ट्रिकल इंजीनियरिंग में ले जाया गया था। यह धारावाहिक IO करने के लिए मुख्य लूप का उपयोग करते हुए टाइमर के अवरोधन का उपयोग करके एक एलईडी को ब्लिंक करने के लिए नमूना आर्डिनो कोड है।

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

लगे:

इंटरप्ट्स चीजों को प्राप्त करने का एक सामान्य तरीका है जबकि कुछ और चल रहा है। नीचे दिए गए उदाहरण में, एलईडी का उपयोग किए बिना झपकी है delay()। जब भी Timer1आग लगती है, तो बाधा सेवा दिनचर्या (ISR) isrBlinker()कहा जाता है। यह एलईडी को चालू / बंद करता है।

यह दिखाने के लिए कि अन्य चीजें एक साथ हो सकती हैं, loop()बार-बार एलईडी ब्लिंकिंग से स्वतंत्र सीरियल पोर्ट के लिए फू / बार लिखती हैं।

#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 अधिक जटिल हो सकता है और टाइमर और बाहरी घटनाओं (पिन) द्वारा ट्रिगर किया जा सकता है। आईएसआर का उपयोग करते हुए कई सामान्य पुस्तकालयों को लागू किया जाता है।


3

मैं मैट्रिक्स एलईडी डिस्प्ले को लागू करते समय भी इस विषय पर आया था।

एक शब्द में, आप Arduino में मिलिस () फ़ंक्शन और टाइमर इंटरप्ट का उपयोग करके एक मतदान शेड्यूलर बना सकते हैं।

मैं बिल अर्ल से निम्नलिखित लेखों का सुझाव देता हूं:

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


2

आप मेरे थ्रेडहैंडलर लाइब्रेरी को भी आजमा सकते हैं

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

यह उपज () या देरी () पर रिले के बिना संदर्भ स्विचिंग की अनुमति देने के लिए एक बाधित अनुसूचक का उपयोग करता है।

मैंने लाइब्रेरी बनाई क्योंकि मुझे तीन थ्रेड्स की आवश्यकता थी और मुझे उनमें से दो की आवश्यकता थी एक सटीक समय पर चलाने के लिए कोई फर्क नहीं पड़ता कि अन्य क्या कर रहे थे। पहले धागे ने धारावाहिक संचार को संभाला। दूसरा Eigen लाइब्रेरी के साथ फ्लोट मैट्रिक्स गुणन का उपयोग करके एक Kalman फ़िल्टर चला रहा था। और तीसरा एक तेज वर्तमान नियंत्रण लूप धागा था जो मैट्रिक्स गणनाओं को बाधित करने में सक्षम होना था।

यह काम किस प्रकार करता है

प्रत्येक चक्रीय धागे की प्राथमिकता और अवधि होती है। यदि एक थ्रेड, वर्तमान निष्पादन थ्रेड की तुलना में उच्च प्राथमिकता के साथ होता है, तो इसका अगला निष्पादन समय पर पहुंच जाता है, तो शेड्यूलर वर्तमान थ्रेड को रोक देगा और उच्च प्राथमिकता वाले पर स्विच कर देगा। एक बार जब उच्च प्राथमिकता वाला धागा पूरा हो जाता है, तो निष्पादनकर्ता पिछले धागे पर वापस आ जाता है।

निर्धारण नियम

थ्रेडहैंडलर लाइब्रेरी की शेड्यूलिंग योजना इस प्रकार है:

  1. सबसे पहले प्राथमिकता।
  2. यदि प्राथमिकता समान है तो सबसे पहले समय सीमा के साथ थ्रेड निष्पादित किया जाता है।
  3. यदि दो थ्रेड्स में एक ही समय सीमा है तो पहले बनाया गया धागा पहले निष्पादित होगा।
  4. एक धागा केवल उच्च प्राथमिकता वाले धागे से बाधित हो सकता है।
  5. एक बार जब कोई थ्रेड निष्पादित हो रहा होता है तो यह रन फंक्शन के वापस आने तक कम प्राथमिकता वाले सभी थ्रेड्स के लिए निष्पादन को रोक देगा।
  6. थ्रेडहैंडलर थ्रेड्स की तुलना में लूप फ़ंक्शन की प्राथमिकता -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::getInstance()->enableThreadExecution();

1

और यहाँ अभी तक एक और माइक्रोप्रोसेसर सहकारी मल्टीटास्किंग लाइब्रेरी है - PQRST: सरल कार्य चलाने के लिए एक प्राथमिकता कतार।

इस मॉडल में, एक धागा को एक उपवर्ग के रूप में लागू किया जाता है Task, जो कुछ भविष्य के समय के लिए निर्धारित होता है (और संभवतः नियमित अंतराल पर पुनर्निर्धारित किया जाता है, यदि, जैसा कि आम है, तो LoopTaskइसके बजाय उपवर्ग होता है )। run()कार्य के कारण हो जाने पर वस्तु की विधि को कहा जाता है। run()विधि कुछ कारण काम करता है, और फिर रिटर्न (इस सहकारी सा है); यह आमतौर पर क्रमिक आवृत्तियों पर अपने कार्यों का प्रबंधन करने के लिए किसी प्रकार की राज्य मशीन को बनाए रखेगा (एक तुच्छ light_on_p_उदाहरण नीचे उदाहरण में चर है)। आपको अपने कोड को कैसे व्यवस्थित किया जाए, इसकी थोड़ी पुनर्विचार की आवश्यकता है, लेकिन काफी गहन उपयोग में यह बहुत ही लचीला और मजबूत साबित हुआ है।

यह समय इकाइयों के बारे में नास्तिक है, तो यह की इकाइयों में चल रहा है के रूप में खुश है millis()के रूप में micros(), या किसी अन्य टिक कि सुविधाजनक है।

यहाँ इस पुस्तकालय का उपयोग करके 'ब्लिंक' कार्यक्रम लागू किया गया है। इससे केवल एक ही कार्य चल रहा है: अन्य कार्य आमतौर पर बनाए जाएंगे, और भीतर शुरू हो जाएंगे 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 मुझे यकीन नहीं है कि आपका क्या मतलब है। run()विधि को कॉल करने के बाद , इसे बाधित नहीं किया जाता है, इसलिए यह उचित रूप से तुरंत समाप्त करने की जिम्मेदारी है। आमतौर पर, हालांकि, यह अपना काम करेगा फिर अपने LoopTaskभविष्य के समय के लिए (संभवतः स्वचालित रूप से, एक उपवर्ग के मामले में ) पुनर्निर्धारित । एक सामान्य पैटर्न कार्य के लिए है कि कुछ आंतरिक राज्य मशीन (एक तुच्छ उदाहरण light_on_p_ऊपर की स्थिति है) को बनाए रखने के लिए, ताकि जब यह अगली बार हो तो उपयुक्त व्यवहार करे।
नॉर्मन ग्रे

तो हाँ, वे रन-टू-कम्प्लीट (RtC) कार्य हैं: कोई भी कार्य वर्तमान में चलने से पहले नहीं चल सकता है, जिसमें से लौटने पर उसका निष्पादन पूरा हो जाता है run()। यह सहकारी धागों के विपरीत है, जिससे सीपीयू द्वारा उपज प्राप्त की जा सकती है, जैसे, कॉलिंग yield()या delay()। या प्रीमेप्टिव थ्रेड्स, जो किसी भी समय शेड्यूल-आउट किए जा सकते हैं। मुझे लगता है कि भेद महत्वपूर्ण है, जैसा कि मैंने देखा है कि बहुत से लोग जो धागे की खोज करने के लिए यहां आते हैं वे ऐसा इसलिए करते हैं क्योंकि वे राज्य मशीनों के बजाय ब्लॉकिंग कोड लिखना पसंद करते हैं। सीपीयू की पैदावार करने वाले असली धागों को ब्लॉक करना ठीक है। आरटीसी कार्यों को अवरुद्ध करना नहीं है।
एडगर बोनट

@EdgarBonet यह एक उपयोगी अंतर है, हाँ। मैं इस शैली, और उपज-शैली के धागे दोनों का संबंध होगा, जैसा कि सहकारी धागे की विभिन्न शैलियों के रूप में, प्रीमेप्टिव थ्रेड्स के विपरीत, लेकिन यह सच है कि उन्हें कोडिंग करने के लिए एक अलग दृष्टिकोण की आवश्यकता होती है। यहां वर्णित विभिन्न दृष्टिकोणों की एक विचारशील और गहराई से तुलना करना दिलचस्प होगा; ऊपर वर्णित एक अच्छा पुस्तकालय प्रोटोथ्रेड्स नहीं है । मुझे दोनों में आलोचना करने के लिए चीजें मिलती हैं, लेकिन प्रशंसा करने के लिए भी। मैं (निश्चित रूप से) मेरे दृष्टिकोण को पसंद करता हूं, क्योंकि यह सबसे स्पष्ट लगता है और अतिरिक्त स्टैक की आवश्यकता नहीं है।
नॉर्मन ग्रे

(सुधार: प्रोटोथ्रेड्स का उल्लेख, @ साचलेन के उत्तर में किया गया था )
नॉर्मन ग्रे
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.