실행중인 여러 스레드를 어떻게 만들 수 있습니까?

동일한 코드 블록에서 여러 작업을 수행하지 않고 프로그램의 여러 부분을 함께 실행할 수있는 방법이 있습니까?

하나 스레드가 외부 장치를 기다리는 동안 다른 스레드의 LED도 깜박입니다.

댓글

  • 스레드가 정말로 필요한지 먼저 스스로에게 물어봐야합니다. 타이머는 이미 필요에 따라 괜찮을 수 있으며 기본적으로 Arduino에서 지원됩니다.
  • Uzebox도 확인하는 것이 좋습니다. ‘는 2 칩 홈브류 비디오 게임 콘솔입니다. 따라서 정확히 Arduino는 아니지만 ‘ 전체 시스템은 인터럽트를 기반으로 구축됩니다. 따라서 오디오, 비디오, 컨트롤 등은 모두 인터럽트로 구동되는 반면 메인 프로그램은 이에 대해 걱정할 필요가 없습니다. ‘ 좋은 참조가 될 수 있습니다.

답변

다중 프로세스 나 다중 스레딩이 없습니다. Arduino. 하지만 일부 소프트웨어로 여러 스레드에 가까운 작업을 수행 할 수 있습니다.

프로토스 레드 를보고 싶습니다.

Protothread는 소형 임베디드 시스템 또는 무선 센서 네트워크 노드와 같이 메모리가 제한적인 시스템을 위해 설계된 매우 가벼운 스택리스 스레드입니다. Protothreads는 C로 구현 된 이벤트 기반 시스템에 선형 코드 실행을 제공합니다. Protothreads는 기본 운영 체제와 함께 사용하거나 사용하지 않고 사용하여 블로킹 이벤트 처리기를 제공 할 수 있습니다. 프로토스 레드는 복잡한 상태 머신이나 전체 멀티 스레딩없이 순차적 인 제어 흐름을 제공합니다.

물론 Arduino 예제가 있습니다. 여기 예제 코드 가 있습니다. 이 SO 질문 도 유용 할 수 있습니다.

ArduinoThread 는 좋은 것입니다.

댓글

답변

AVR 기반 Arduino “는 (하드웨어) 스레딩을 지원하지 않습니다. 저는 ARM 기반 Arduino”에 익숙하지 않습니다. 이 제한을 우회하는 한 가지 방법은 인터럽트, 특히 시간이 지정된 인터럽트를 사용하는 것입니다. 특정 다른 루틴을 실행하기 위해 수 마이크로 초마다 메인 루틴을 중단하도록 타이머를 프로그래밍 할 수 있습니다.

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

답변

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

각 함수는 LED 조작을 수행하는 function1 및 부동 계산을 수행하는 function2와 같은 별도의 작업을 수행 할 수 있습니다. 할당 된 시간을 지키는 것은 각 작업 (기능)의 책임입니다.

이것이 시작하기에 충분합니다.

댓글

  • ” 스레드 비선 점형 스케줄러를 사용하는 경우. 그런데 이러한 스케줄러는 이미 arduino 라이브러리로 존재합니다. arduino.cc/en/Reference/Scheduler
  • @jfpoilpret-Cooperative 멀티 스레딩은 진짜입니다.
  • 예 ‘ 맞습니다! 내 실수; 너무 오래 전부터 협업 멀티 스레딩에 직면하지 않았기 때문에 멀티 스레딩은 선점 적이어야했습니다.

답변

요구 사항 설명에 따라 :

  • 외부 장치를 기다리는 스레드 하나
  • LED가 깜박이는 스레드 하나

첫 번째 “스레드”에 대해 하나의 Arduino 인터럽트를 사용할 수있는 것 같습니다 (실제로 “작업”이라고 부릅니다).

Arduino 인터럽트는 외부를 기반으로 하나의 함수 (사용자 코드)를 호출 할 수 있습니다. 이벤트 (디지털 입력 핀의 전압 레벨 또는 레벨 변경)는 즉시 기능을 트리거합니다.

그러나 인터럽트와 관련하여 염두에 두어야 할 한 가지 중요한 점은 호출 된 기능이 최대한 빨라야한다는 것입니다. (일반적으로 delay() 호출이나 delay()에 의존하는 다른 API가 없어야합니다).

외부 이벤트 트리거시 활성화 할 긴 작업이있는 경우 잠재적으로 협력 스케줄러를 사용하고 인터럽트 기능에서 새 작업을 추가 할 수 있습니다.

두 번째 중요합니다. 인터럽트에 대한 요점은 그 수가 제한되어 있다는 것입니다 (예 : UNO에서는 2 개만). 따라서 더 많은 외부 이벤트를 갖기 시작하면 모든 입력을 하나로 멀티플렉싱하고 인터럽트 함수가 실제 트리거가 무엇인지 결정하도록해야합니다.

Answer

간단한 해결책은 스케줄러 를 사용하는 것입니다. 몇 가지 구현이 있습니다. 이것은 AVR 및 SAM 기반 보드에서 사용할 수있는 간단한 설명입니다. 기본적으로 단일 호출로 작업이 시작됩니다. “스케치 내 스케치”.

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

Scheduler.start ()는 taskSetup을 한 번 실행 한 다음 반복적으로 taskLoop을 호출하는 새 작업을 추가합니다. Arduino 스케치가 작동합니다. 작업에는 자체 스택이 있습니다. 스택의 크기는 선택적 매개 변수입니다. 기본 스택 크기는 128 바이트입니다.

컨텍스트 전환을 허용하려면 작업에서 yield () 또는 delay ( ) . 조건 대기를위한 지원 매크로도 있습니다.

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

자세한 내용은 를 참조하세요. 여러 LED 깜박임에서 디 바운스 버튼까지의 예와 비 차단 명령 줄 읽기가있는 간단한 셸이 있습니다. 템플릿과 네임 스페이스를 사용하여 소스 코드를 구조화하고 줄일 수 있습니다. 아래 sketch 는 다중 깜박임을위한 템플릿 기능을 사용하는 방법을 보여줍니다. 스택에 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(); } 

성능, 즉 시간에 대한 아이디어를 제공하는 벤치 마크 도 있습니다. 작업 시작, 컨텍스트 전환 등.

마지막으로 작업 수준 동기화 및 통신을위한 몇 가지 지원 클래스가 있습니다. 대기열 세마포어 .

답변

매트릭스 LED 디스플레이를 구현하는 동안에도이 주제를 다루었습니다.

한 마디로 , Arduino에서 millis () 함수와 타이머 인터럽트를 사용하여 폴링 스케줄러를 구축 할 수 있습니다.

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

답변

이 포럼의 이전 주문에서 다음 질문 / 답변이 전기 공학으로 이동되었습니다. 메인 루프를 사용하여 직렬 IO를 수행하는 동안 타이머 인터럽트를 사용하여 LED를 깜박이는 샘플 arduino 코드가 있습니다.

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

재 게시 :

인터럽트는 다른 작업이 진행되는 동안 작업을 완료하는 일반적인 방법입니다. 아래 예에서 LED는 delay()를 사용하지 않고 깜박입니다. Timer1가 실행될 때마다 ISR (인터럽트 서비스 루틴) isrBlinker()가 호출됩니다. LED를 켜거나 끕니다.

다른 일이 동시에 발생할 수 있음을 보여주기 위해 loop()는 LED 깜박임과 관계없이 직렬 포트에 foo / bar를 반복적으로 기록합니다. .

#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은 훨씬 더 복잡 할 수 있으며 타이머 및 외부 이벤트 (핀)에 의해 트리거 될 수 있습니다. 대부분의 공통 라이브러리는 ISR을 사용하여 구현됩니다.

Answer

내 ThreadHandler 라이브러리를 사용해 볼 수도 있습니다.

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

중계하지 않고 컨텍스트 전환을 허용하기 위해 인터럽트 스케줄러를 사용합니다. yield () 또는 delay ().

3 개의 스레드가 필요했고 다른 스레드가 무엇을하든 정확한 시간에 실행하려면 스레드 2 개가 필요했기 때문에 라이브러리를 만들었습니다. 첫 번째 스레드는 직렬 통신을 처리했습니다. 두 번째는 Eigen 라이브러리와 함께 부동 행렬 곱셈을 사용하여 칼만 필터를 실행하는 것입니다. 세 번째는 행렬 계산을 중단 할 수 있어야하는 빠른 전류 제어 루프 스레드였습니다.

작동 원리

각 순환 스레드에는 우선 순위와 기간이 있습니다. 현재 실행중인 스레드보다 우선 순위가 높은 스레드가 다음 실행 시간에 도달하면 스케줄러는 현재 스레드를 일시 중지하고 우선 순위가 높은 스레드로 전환합니다. 우선 순위가 높은 스레드가 실행을 완료하면 스케줄러는 이전 스레드로 다시 전환됩니다.

스케줄링 규칙

ThreadHandler 라이브러리의 스케줄링 체계는 다음과 같습니다.

  1. 가장 높은 우선 순위가 우선입니다.
  2. 우선 순위가 같으면 기한이 가장 빠른 스레드가 먼저 실행됩니다.
  3. 두 개의 스레드가 기한이 같으면 처음 작성된 스레드가 먼저 실행됩니다.
  4. 스레드는 우선 순위가 높은 스레드에 의해서만 중단 될 수 있습니다.
  5. 스레드가 실행되면 실행 함수가 반환 될 때까지 우선 순위가 낮은 모든 스레드의 실행을 차단합니다.
  6. 루프 함수는 ThreadHandler 스레드에 비해 우선 순위가 -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 });  

Thread 객체는 생성 될 때 ThreadHandler에 자동으로 연결됩니다.

생성 된 스레드 객체의 실행을 시작하려면 다음을 호출합니다.

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

An swer

또 다른 마이크로 프로세서 협력 멀티 태스킹 라이브러리 인 PQRST : 단순 작업 실행을위한 우선 순위 대기열

여기 모델에서 스레드는 Task의 하위 클래스로 구현됩니다.이 하위 클래스는 미래에 예정되어 있습니다 (일반적으로

). 작업이 만료되면 개체의 run() 메서드가 호출됩니다. run() 메서드는 적절한 작업을 수행 한 다음 반환합니다 (이는 협력 비트입니다). 일반적으로 연속 호출에 대한 작업을 관리하기 위해 일종의 상태 머신을 유지합니다 (아래 예의 간단한 예는 light_on_p_ 변수입니다). 방법을 약간 재고해야합니다. 코드를 구성하지만 상당히 집약적 인 사용에서 매우 유연하고 견고 함이 입증되었습니다.

시간 단위에 구애받지 않으므로 millis() as micros() 또는 기타 편리한 눈금.

다음은이 라이브러리를 사용하여 구현 된 blink프로그램입니다. 실행중인 작업 하나만 표시됩니다. 다른 작업은 일반적으로 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 I ‘ 무슨 뜻인지 잘 모르겠습니다. run() 메서드가 호출 된 후에는 중단되지 않으므로 합리적으로 신속하게 완료 할 책임이 있습니다. 그러나 일반적으로 ‘ 작업을 수행 한 다음 자체 일정을 다시 예약합니다 (LoopTask의 하위 클래스의 경우 자동으로). 미래의 시간. 일반적인 패턴은 작업이 내부 상태 머신 (위의 light_on_p_ 상태)을 유지하여 다음 기한이 될 때 적절하게 작동하도록하는 것입니다.
  • 맞습니다. RtC (Run-to-Completion) 작업입니다. 현재 작업이 run()에서 반환하여 실행을 완료하기 전에는 작업을 실행할 수 없습니다. 이는 yield() 또는 delay()를 호출하여 CPU를 산출 할 수있는 협력 스레드와 대조됩니다. 또는 언제든지 예약 할 수있는 선점 스레드입니다. 여기에서 스레드를 검색하는 많은 사람들이 상태 머신보다 차단 코드를 작성하는 것을 선호하기 때문에 그렇게하는 것을 보았 기 때문에 구별이 중요하다고 생각합니다. CPU를 생성하는 실제 스레드를 차단하는 것은 괜찮습니다. RtC 작업을 차단하는 것은 아닙니다.
  • @EdgarBonet It ‘ 유용한 구별입니다. 저는이 스타일과 yield-style 스레드를 선점 형 스레드와 달리 단순히 다른 스타일의 협력 스레드로 간주하지만 ‘에 대해 다른 접근 방식이 필요하다는 것은 사실입니다. 코딩. 여기에 언급 된 다양한 접근 방식을 신중하고 심층적으로 비교하는 것은 흥미로울 것입니다. 위에 언급되지 않은 멋진 라이브러리 중 하나는 protothreads 입니다. 나는 두 가지 모두에서 비판 할 것이나 칭찬 할 것을 발견합니다. 나는 (물론) 내 접근 방식을 선호합니다. 가장 명시적이고 추가 스택이 필요하지 않기 때문입니다.
  • (수정 : protothreads는 입니다.

@sachleen ‘의 답변 )

답글 남기기

이메일 주소를 발행하지 않을 것입니다. 필수 항목은 *(으)로 표시합니다