একই কোড ব্লকে একাধিক কাজ না করে প্রোগ্রামের একাধিক অংশ একসাথে চলার উপায় আছে কি?
একটি থ্রেড বাহ্যিক ডিভাইসের জন্য অপেক্ষায় যখন অন্য থ্রেডে একটি এলইডি জ্বলজ্বল করে।
একই কোড ব্লকে একাধিক কাজ না করে প্রোগ্রামের একাধিক অংশ একসাথে চলার উপায় আছে কি?
একটি থ্রেড বাহ্যিক ডিভাইসের জন্য অপেক্ষায় যখন অন্য থ্রেডে একটি এলইডি জ্বলজ্বল করে।
উত্তর:
আরডুইনোতে কোনও মাল্টি-প্রক্রিয়া বা মাল্টি-থ্রেডিং সমর্থন নেই। যদিও কিছু সফ্টওয়্যার দিয়ে আপনি একাধিক থ্রেডের কাছাকাছি কিছু করতে পারেন।
আপনি প্রোটোথ্রেডগুলি দেখতে চান :
প্রোটোথ্রেডগুলি হ'ল এমবেডেড সিস্টেম বা ওয়্যারলেস সেন্সর নেটওয়ার্ক নোডের মতো মারাত্মক মেমোরি সীমাবদ্ধ সিস্টেমগুলির জন্য ডিজাইন করা অত্যন্ত হালকা স্ট্যাকলেস থ্রেড। প্রোটোথ্রেডগুলি সি প্রয়োগ করা ইভেন্ট-চালিত সিস্টেমগুলির জন্য রৈখিক কোড সম্পাদন সরবরাহ করে প্রোটোথ্রেডগুলি ব্লকিং ইভেন্ট হ্যান্ডলারগুলি সরবরাহ করতে অন্তর্নিহিত অপারেটিং সিস্টেমের সাথে বা ছাড়াই ব্যবহার করা যেতে পারে। প্রোটোথ্রেডগুলি জটিল রাষ্ট্রের মেশিন বা সম্পূর্ণ মাল্টি-থ্রেডিং ছাড়াই নিয়মিত ক্রমের প্রবাহ সরবরাহ করে।
অবশ্যই এখানে উদাহরণ কোড সহ একটি আরডুইনো উদাহরণ রয়েছে । এই এই প্রশ্নটিও কার্যকর হতে পারে।
আরডুইনো থ্রেডও খুব ভাল।
এভিআর ভিত্তিক আরডিনোগুলি (হার্ডওয়্যার) থ্রেডিং সমর্থন করে না, আমি এআরএম ভিত্তিক আরডুইনোর সাথে অপরিচিত। এই সীমাবদ্ধতার চারপাশের একটি উপায় হ'ল বিঘ্ন ব্যবহার, বিশেষত সময়ের ব্যবধানগুলি rup একটি নির্দিষ্ট অন্যান্য রুটিন চালানোর জন্য আপনি প্রতি এত এত মাইক্রোসেকেন্ডে প্রধান রুটিন বাধাগ্রস্থ করতে একটি টাইমার প্রোগ্রাম করতে পারেন।
ইউনোতে সফ্টওয়্যার সাইড মাল্টি-থ্রেডিং করা সম্ভব। হার্ডওয়্যার স্তর থ্রেডিং সমর্থিত নয়।
মাল্টিথ্রেডিং অর্জনের জন্য, চালানোর প্রয়োজন হয় এমন বিভিন্ন কাজগুলি ট্র্যাক করার জন্য এটি একটি বেসিক শিডিয়ুলার বাস্তবায়ন এবং একটি প্রক্রিয়া বা টাস্ক তালিকা বজায় রাখা প্রয়োজন।
খুব সাধারণ অ-প্রাক-প্রাক-শিডিয়ুলারের কাঠামোটি এরকম হবে:
//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
ভাসমান গণনা করা। এটি প্রতিটি কাজের (ক্রিয়াকলাপ) এর জন্য নির্ধারিত সময় মেনে চলা দায়বদ্ধ হবে।
আশা করি, আপনি শুরু করার জন্য এটি যথেষ্ট হওয়া উচিত।
আপনার প্রয়োজনীয়তার বিবরণ অনুসারে:
দেখে মনে হচ্ছে আপনি প্রথম "থ্রেড" এর জন্য একটি আরডুইনো বাধা ব্যবহার করতে পারেন (আমি বরং এটিকে "টাস্ক" বলব)।
আরডুইনো বিঘ্ন একটি বাহ্যিক ইভেন্টের ভিত্তিতে একটি ফাংশন (আপনার কোড) কল করতে পারে (ডিজিটাল ইনপুট পিনের ভোল্টেজের স্তর বা স্তর পরিবর্তন), যা তত্ক্ষণাত আপনার ক্রিয়াকে ট্রিগার করবে।
তবে, বিঘ্নের সাথে মনে রাখার একটি গুরুত্বপূর্ণ বিষয় হ'ল ডাকা ফাংশনটি যত তাড়াতাড়ি দ্রুত হওয়া উচিত (সাধারণত, কোনও delay()
কল বা অন্য কোনও এপিআই থাকা উচিত নয় যা নির্ভর করে delay()
)।
যদি আপনার বাহ্যিক ইভেন্ট ট্রিগার সক্রিয় করার জন্য একটি দীর্ঘ কাজ থাকে তবে আপনি সম্ভাব্যভাবে একটি সমবায় শিডিয়ুলার ব্যবহার করতে পারেন এবং আপনার বাধা ফাংশন থেকে এটিতে একটি নতুন কার্য যুক্ত করতে পারেন।
বাধা সম্পর্কে একটি দ্বিতীয় গুরুত্বপূর্ণ বিষয় হ'ল তাদের সংখ্যা সীমিত (যেমন ইউএনওতে কেবল 2)। সুতরাং আপনি যদি আরও বাহ্যিক ইভেন্টগুলি টোভ করতে শুরু করেন তবে আপনাকে একরকম একাধিক মাল্টিপ্লেক্সিং প্রয়োগ করতে হবে এবং আপনার বাধা ফাংশনটি নির্ধারণ করতে হবে যে মাল্টিপ্লেক্স ইনট প্রকৃত ট্রিগার ছিল।
একটি সহজ সমাধান হল একটি শিডিয়ুলার ব্যবহার করা । বেশ কয়েকটি বাস্তবায়ন রয়েছে। এটি খুব শীঘ্রই এমন একটি বর্ণনা করে যা AVR এবং SAM ভিত্তিক বোর্ডগুলির জন্য উপলভ্য। মূলত একটি একক কল একটি কাজ শুরু করবে; "একটি স্কেচের মধ্যে স্কেচ"।
#include <Scheduler.h>
....
void setup()
{
...
Scheduler.start(taskSetup, taskLoop);
}
শিডিয়ুলআরস্টার্ট () একটি নতুন টাস্ক যুক্ত করবে যা একবার টাস্কসেটআপটি চালাবে এবং তারপরে বার বার টাস্কলুপকে কল করবে ঠিক যেমন আরডিনো স্কেচ কাজ করে। টাস্কটির নিজস্ব স্ট্যাক রয়েছে। স্ট্যাকের আকারটি একটি alচ্ছিক প্যারামিটার। ডিফল্ট স্ট্যাকের আকার 128 বাইট।
প্রসঙ্গের স্যুইচিংয়ের জন্য কার্যগুলিকে অনুমতি দেওয়ার জন্য ফলন () বা বিলম্ব () করতে হবে । শর্তের জন্য অপেক্ষা করার জন্য একটি সমর্থন ম্যাক্রোও রয়েছে।
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);
}
আরও তথ্যের জন্য উদাহরণগুলি দেখুন । একাধিক এলইডি ঝলক থেকে শুরু করে ডিবাউশন বোতাম এবং অ-ব্লকিং কমান্ড লাইনের সাথে একটি সাধারণ শেল রয়েছে examples টেমপ্লেট এবং নেমস্পেসগুলি কাঠামো সহায়তা করতে এবং উত্স কোড হ্রাস করতে ব্যবহার করা যেতে পারে। নীচের স্কেচটি দেখায় যে মাল্টি-ব্লিঙ্কের জন্য কীভাবে টেমপ্লেট ফাংশন ব্যবহার করতে হয়। স্ট্যাকের জন্য এটি 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();
}
পারফরম্যান্স সম্পর্কে কিছু ধারণা দেওয়ার জন্য একটি বেঞ্চমার্কও রয়েছে , যেমন টাস্ক শুরু করার সময়, প্রসঙ্গের সুইচ ইত্যাদি of
সর্বশেষে, টাস্ক স্তরের সিঙ্ক্রোনাইজেশন এবং যোগাযোগের জন্য কয়েকটি সমর্থন ক্লাস রয়েছে; সারি এবং সেমফোর্ ।
এই ফোরামের পূর্ববর্তী জ্বলন থেকে, নিম্নলিখিত প্রশ্ন / উত্তরটি বৈদ্যুতিক প্রকৌশলে স্থানান্তরিত হয়েছিল। এটিতে সিরিয়াল আইও করার জন্য প্রধান লুপটি ব্যবহার করার সময় টাইমার বিঘ্নিত একটি এলইডি জ্বলতে একটি নমুনা আরডুইনো কোড রয়েছে।
রিপোস্ট করুন:
বাধা হ'ল অন্য কিছু চলার সময় জিনিসগুলি সম্পন্ন করার একটি সাধারণ উপায়। নীচের উদাহরণে, LED ব্যবহার না করে জ্বলজ্বল করছে delay()
। যখনই Timer1
আগুন লাগে তখন ইন্টারপ্রেট সার্ভিস রুটিন (আইএসআর) isrBlinker()
বলা হয়। এটি LED চালু / বন্ধ করে দেয়।
অন্যান্য জিনিস একই সাথে ঘটতে পারে তা দেখানোর জন্য, loop()
বারবার এলইডি জ্বলজ্বলে পৃথকভাবে সিরিয়াল বন্দরে foo / বার লিখে।
#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);
}
এটি একটি খুব সাধারণ ডেমো। আইএসআরগুলি আরও জটিল হতে পারে এবং টাইমার এবং বাহ্যিক ইভেন্টগুলি (পিনগুলি) দ্বারা ট্রিগার করা যায়। প্রচলিত লাইব্রেরিগুলির অনেকগুলি আইএসআর ব্যবহার করে প্রয়োগ করা হয়।
আমি ম্যাট্রিক্স এলইডি ডিসপ্লে বাস্তবায়নের সময়ও এই বিষয়টিতে এসেছি।
এক কথায়, আপনি আরডুইনোতে মিলিস () ফাংশন এবং টাইমার বিঘ্নিত ব্যবহার করে একটি পোলিং শিডিয়ুলার তৈরি করতে পারেন।
আমি বিল আর্ল থেকে নিম্নলিখিত নিবন্ধগুলি পরামর্শ দিই:
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
আপনি আমার থ্রেডহ্যান্ডলার গ্রন্থাগারটিও চেষ্টা করে দেখতে পারেন
https://bitbucket.org/adamb3_14/threadhandler/src/master/
ফলন () বা বিলম্ব () এ রিলে না করে প্রসঙ্গের স্যুইচিংয়ের অনুমতি দেওয়ার জন্য এটি একটি বিঘ্নিত শিডিয়ুলার ব্যবহার করে।
আমি লাইব্রেরিটি তৈরি করেছি কারণ আমার তিনটি থ্রেডের প্রয়োজন ছিল এবং অন্যেরা যা করছিল তা বিবেচনা করে একটি নিখুঁত সময়ে চালানোর জন্য আমার দু'জনের দরকার ছিল। প্রথম থ্রেড পরিচালিত সিরিয়াল যোগাযোগ। দ্বিতীয়টি ইগেন লাইব্রেরির সাথে ফ্লোট ম্যাট্রিক্সের গুণটি ব্যবহার করে একটি কলম্যান ফিল্টার চালাচ্ছিল। এবং তৃতীয়টি ছিল একটি দ্রুত কারেন্ট কন্ট্রোল লুপ থ্রেড যা ম্যাট্রিক্স গণনাগুলিতে বাধা দিতে সক্ষম হয়েছিল।
প্রতিটি চক্রীয় থ্রেডের একটি অগ্রাধিকার এবং একটি সময়কাল থাকে। যদি কোনও থ্রেড, বর্তমান নির্বাহক থ্রেডের চেয়ে উচ্চতর অগ্রাধিকার সহ, তার পরবর্তী সম্পাদনের সময় পৌঁছে যায় তবে শিডিয়ুলার বর্তমান থ্রেডটি থামিয়ে উচ্চতর অগ্রাধিকারের দিকে স্যুইচ করবে। একবার উচ্চ অগ্রাধিকারের থ্রেডটি কার্যকর করা শেষ করে সিডিউলটি পূর্ববর্তী থ্রেডে ফিরে যায়।
থ্রেডহ্যান্ডলার লাইব্রেরির সময়সূচী স্কিমটি নিম্নরূপ:
থ্রেডগুলি সি ++ উত্তরাধিকারের মাধ্যমে তৈরি করা যেতে পারে
class MyThread : public Thread
{
public:
MyThread() : Thread(priority, period, offset){}
virtual ~MyThread(){}
virtual void run()
{
//code to run
}
};
MyThread* threadObj = new MyThread();
অথবা ক্রিয়েট্রেড এবং ল্যাম্বডা ফাংশনের মাধ্যমে
Thread* myThread = createThread(priority, period, offset,
[]()
{
//code to run
});
থ্রেড অবজেক্টস তৈরি হয়ে গেলে স্বয়ংক্রিয়ভাবে থ্রেডহ্যান্ডলারের সাথে সংযোগ স্থাপন করে।
তৈরি থ্রেড অবজেক্টগুলির কল কার্যকর করতে শুরু করুন:
ThreadHandler::getInstance()->enableThreadExecution();
এবং এখানে আরও একটি মাইক্রোপ্রসেসর সমবায় মাল্টিটাস্কিং গ্রন্থাগার রয়েছে - পিকিউআরএসটি: সাধারণ টাস্কগুলি চালনার জন্য একটি অগ্রাধিকার সারি।
এই মডেলটিতে, একটি থ্রেড এ এর সাবক্লাস হিসাবে প্রয়োগ করা হয়েছে 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());
}
run()
পদ্ধতিটি কল করার পরে , এটি বাধাগ্রস্ত হয় না, সুতরাং যুক্তিযুক্তভাবে অবিলম্বে শেষ করার দায়িত্ব রয়েছে। সাধারণত, তবে এটি তার কাজটি করবে এবং তারপরে LoopTask
ভবিষ্যতের কিছু সময়ের জন্য নিজেকে পুনঃনির্ধারণ করবে (সম্ভবত স্বয়ংক্রিয়ভাবে, একটি সাবক্লাসের ক্ষেত্রে )। একটি সাধারণ প্যাটার্ন হ'ল টাস্কটির জন্য কিছু অভ্যন্তরীণ রাষ্ট্রীয় মেশিন বজায় রাখা (একটি তুচ্ছ উদাহরণের light_on_p_
উপরের রাজ্যটি) যাতে পরবর্তী কারণে যখন এটি উপযুক্তভাবে আচরণ করে।
run()
। এটি সমবায় থ্রেডের বিপরীতে, যা সিপিইউ অর্জন করতে পারে, যেমন, কলিং yield()
বা delay()
। বা প্রিমিটিভ থ্রেডগুলি, যে কোনও সময় নির্ধারিত হতে পারে। আমি এই পার্থক্যটি গুরুত্বপূর্ণ বলে মনে করি, যেমন আমি দেখেছি যে অনেক লোক যারা এখানে থ্রেড অনুসন্ধান করতে চলেছে তারা এটি করে কারণ তারা রাষ্ট্রীয় মেশিনগুলির চেয়ে ব্লকিং কোড লেখাকে পছন্দ করে। আসল থ্রেডগুলি ব্লক করা যা সিপিইউ দেয় ভাল is আরটিসি টাস্কগুলি ব্লক করা নয়।