マむクロコントロヌラヌでの連続性ベヌスのマルチタスク

ずにかく、Cプログラマヌは蚀語機胜に甘やかされず、マむクロコントロヌラヌの組み蟌みシステムの開発者はさらに制限され、倚くの堎合、プログラムはOSのサポヌトなしでベアメタルで動䜜したす。

Cでコルヌチン、ゞェネレヌタヌ、および協調マルチタスクを䜿甚する可胜性は、プログラムを倧幅に簡玠化し、匷床を節玄するこずができたすが、これらの蚀語機胜は明らかではなく、倚くはそれらに぀いお知りたせん。

継続contionuationを䜿甚するず、プログラムストリヌム関数の実行状態を蚘憶し、将来この堎所に戻るこずができたす。

拡匵機胜を䜿甚するず、ほずんど既補のゞェネレヌタヌ 、 むテレヌタヌ、 協調マルチタスクであるコルヌチンを取埗できたす。



これらのメ゜ッドを䜿甚するC拡匵機胜ずサンプルラむブラリを実装する方法は次のずおりです。



この蚘事では、Adam Dunkelsのprotothreadsラむブラリヌを調べたす。 シンプルでミニマルで、いく぀かのヘッダヌファむルのマクロのセットで構成され、C ++ず互換性がありたす。 ラむブラリには必芁なものがすべお含たれおおり、他のラむブラリ、プラットフォヌム、コンパむラに䟝存したせん。 動的メモリを䜿甚したせん。 gccがある堎合、ラむブラリは倉数ラベルを䜿甚しお远加機胜を提䟛したす。 䞀般に、このラむブラリは、さたざたなアヌキテクチャ、コンパむラ、およびオペレヌティングシステムがある組み蟌みアプリケヌションのプログラミングに最も䟿利であるように思われたした。

ラむブラリの動䜜方法に぀いおは、そのWebサむトで詳しく説明されおいたす。APIの簡単な説明ず䜿甚䟋をいく぀か玹介したす。 もちろん、ラむブラリの䜿甚は単なる「シンタックスシュガヌ」であり、たずえばステヌトマシンを䜿甚しお実行できたす実際、ラむブラリは関数を暗黙的なステヌトマシンに倉換したす。 しかし、倚くのタスクにおいお、ラむブラリはプログラムテキストの量を枛らし、盎線性、明瞭性を高め、その結果、開発時間ず゚ラヌの可胜性を枛らすこずができたす。 ステヌトマシンを䜿甚しお問題を解決し、ダヌス以䞊のステヌトがある堎合、それらの倚くは順番に進みたす。ほずんどの堎合、プロトスレッドは人生を倧幅に簡玠化できたす。 私の経隓では、プロトスレッドを䜿甚するプログラムの゜ヌスコヌドは、ステヌトマシンを䜿甚しお蚘述された同等のものよりも玄20〜50少なくなりたす。



この蚘事の䟋はWindowsで曞かれおおり、MinGWずVisual Studioで動䜜したすが、泚意しおください Visual StudioのDEBUG構成では、protothreadsラむブラリはそのたたではコンパむルされたせん

理由は、VSのDEBUG構成の__LINE__マクロが䜕らかの理由で定数から関数呌び出しに倉わるためです。これは、lc-switch.hファむルが眮き換えられた堎合に簡単に凊理されたす。

#define LC_SET(s) s = __LINE__; case __LINE__:
      
      





に

 #define LC_SET(s) s = __COUNTER__+1; case (__COUNTER__):
      
      





gccでは、すべおのプラットフォヌムずプロセッサで、ラむブラリは倉曎なしで機胜したす。

ラむブラリを䜿甚するには、.hファむルをダりンロヌドしおプロゞェクトに解凍し、ヘッダヌを含める必芁がありたす。
 #include "pt.h"
      
      





それで、ラむブラリから䜕を埗るこずができたす

継続



継続自䜓はあたり有甚ではありたせんが、他のプログラム構造の基瀎ずなっおいたす。

続行する機胜を持぀関数は、次のように宣蚀されたす。

 int name(struct pt *pt [,  ])
      
      





たたは、次のようなマクロを䜿甚したす。

 PT_THREAD(name(struct pt *pt [,  ]))
      
      





pt匕数は継続コンテキストぞのポむンタであり、䜕らかの方法で関数が䞭断された堎所を保存したす。

関数は、参照により、远加の匕数を介しお結果を返すこずができたす。

ステヌトメントの開始前に、ストリヌムの最埌にPT_BEGINpt-PT_ENDptがなければなりたせん。 各関数呌び出しでいく぀かのアクションを実行する必芁がある堎合、マクロPT_BEGINptの前に配眮されたす。 これには、たずえば、タむマヌ、カりンタヌなどの倀を曎新するこずができたす。

PT_YIELDptコマンドは結果を返し、次回関数が呌び出されるず、関数は次のステヌトメントから続行したす。

ストリヌムの䞭断たずえば、゚ラヌの堎合は、マクロPT_EXITptによっお実行されたす。 ストリヌムの最埌に到達した埌、たたは割り蟌みマクロPT_ENDptおよびPT_EXITptの埌、 PT_RESTARTptコマンドで再起動するず、関数は最初に「巻き戻され」、次の呌び出しで再び動䜜を開始したす。

関数は定数を返したす-関数の䞭断の理由 PT_WAITING-むベントを埅぀、 PT_YIELDED-倀を返す 、 PT_EXITED - PT_EXITpt コマンドで終了する、 PT_ENDED-関数がPT_ENDptの終わりに達したした。

継続を初めお䜿甚する前に、 PT_INITptマクロでコンテキストを初期化する必芁がありたす。

呌び出し間で倀を保持する必芁がある倉数は、静的ずしお宣蚀するか、参照によっお関数に枡す必芁がありたす。

発電機



耇雑なアルゎリズムでいく぀かのデヌタのシヌケンスを取埗する必芁があり、次の各芁玠を取埗する方法が特定の条件ず以前の倀に䟝存する堎合、ゞェネレヌタヌを䜿甚するず䟿利です。

フィボナッチ数を生成する䟋を次に瀺したす。

 #include "pt.h" #include <stdio.h> static int fib(struct pt *pt, int max, int* res) { static int a,b; PT_BEGIN(pt); a=0;b=1; while (a<max) { *res=a; PT_YIELD(pt); b+=a; a=ba; } PT_END(pt); } int main(void) { int value; struct pt pt1; PT_INIT(&pt1); while( fib(&pt1, 1000, &value) < PT_EXITED ){ printf("Value %d\n",value); } return 0; }
      
      





コルヌチン



叀兞的なコルヌチンは、互いに制埡を移し、割り蟌み䜍眮に戻るこずができるプログラムスレッドです。 コルヌチンは、いく぀かのプログラムフロヌ関数の䞭で、メむンプログラムずサブプログラムの違いが明確でない堎合に䜿甚されたすKnut、「The Art of Programming」の第1巻。 たずえば、プログラムの䞀郚が䜕らかの圢でデヌタを生成し、別の郚分が䜕らかの方法でデヌタを消費したす。 さらに、消費者ずメヌカヌの䞡方が非垞に耇雑です。 デヌタプロデュヌサヌプログラムを䜜成するずき、デヌタの準備ができたら関数ずしおデヌタ消費を呌び出したす。 しかし、デヌタコンシュヌマをプログラムするずきは、すでにコンシュヌマをメむンプログラムにしお、デヌタの受信ず凊理が可胜なずきに生成関数を呌び出したいず思っおいたす。 この堎合、プログラムずサブプログラムの関係の代わりに、コルヌチンずコルヌチンの関係が非垞に適切です。 最初にメモリ内のすべおのデヌタを生成し、その埌すぐにすべおを消費するこずもできたすが、特に組み蟌みシステムでは、メモリよりも倚くのデヌタが存圚する堎合がありたす。 コルヌチンも継続により簡単に取埗できたす。

たずえば、倧量のxmlデヌタを生成しおからアヌカむブし、ファむルに保存するか、ネットワヌク経由で転送する必芁がありたす。 簡単にするために、生成ずストレヌゞのみを実装したす。 メモリおよびファむル操䜜に関するさたざたな゚ラヌチェックも省略されたす。 makeXmlLineは圓瀟のメヌカヌです。 入力文を解析し、呌び出しごずにXML文字列を生成したす。 writeXmlLineはコンシュヌマヌであり、呌び出しごずに行をファむルに保存したす。 コルヌチンは、メヌカヌのコルヌチンが終了するたで結果PT_EXITEDを返すたで1぀ず぀呌び出されたす。



 #include "pt.h" #include <stdio.h> #include <string.h> #include <malloc.h> static int makeXmlLine(struct pt *pt, char* dst, char* src) { static char* text; //   static         char * pch; PT_BEGIN(pt); strcpy(dst,"<?xml version=\"1.0\" encoding=\"Windows-1251\"?>"); PT_YIELD(pt); strcpy(dst,"<text>"); PT_YIELD(pt); text=strdup(src); // strtok   ,    pch = strtok (text," ,.!?:"); while (pch != NULL){ sprintf(dst," <word>%s</word>",pch); PT_YIELD(pt); pch = strtok (NULL, " ,.!?:"); } strcpy(dst,"</text>"); PT_YIELD(pt); *dst=0; //     ,     PT_YIELD(pt); free(text); PT_END(pt); } static int writeXmlLine(struct pt *pt, char* fileName, char* str) { static FILE* file; PT_BEGIN(pt); file=fopen(fileName,"w"); //         while(*str){ fprintf(file,"%s\n",str); PT_YIELD(pt); } fclose(file); PT_END(pt); } int main(void) { struct pt pt1,pt2; char xmlString[128]; PT_INIT(&pt1); PT_INIT(&pt2); //    ,     while (makeXmlLine(&pt1,xmlString, "Hello, world! Pleased to meet you!") < PT_EXITED) writeXmlLine(&pt2,"file.xml",xmlString); return 0; }
      
      





マルチタスク



組み蟌みシステムでは、ポヌトに到着するデヌタ、バッファを解攟する、タむマヌをトリガヌする、別の操䜜を完了する、ボタンを抌す、䞀時停止など、垞に䜕かを埅぀必芁がありたす。 マルチタスクを䜿甚するず、非同期プログラミングモデルから同期プログラミングモデルに移行できたす。 これが適切かどうかは、状況によっお異なりたす。

単玔なスレッドマネヌゞャヌずコルヌチンを䜿甚しお、協調マルチタスクを簡単に取埗できたす。 最も単玔なケヌスでは、ディスパッチャヌはコルヌチンを順次呌び出す無限ルヌプです。 より耇雑なディスパッチャの堎合、タスクの远加/削陀、優先床システムなどを実装できたす。

コルヌチンベヌスのマルチタスクを䜿甚するず、プリ゚ンプティブマルチタスクよりも次の利点がありたす。



マルチタスク甚に、Protothreadsは次のプリミティブを提䟛したす。



倚数の同䞀のストリヌムを開始する堎合は、構造関数ぞのリンクをストリヌム関数に枡したす。ストリヌム関数は、ストリヌムが独自のストリヌムに䜿甚するすべおのデヌタず倉数を栌玍したす。 単䞀スレッドの堎合、すべおの倉数を静的ずしお宣蚀するだけで、関数内に保存できたす。

マルチスレッドの䟋ずしお、りィンドり甚のシンプルなコン゜ヌルArkanoid100行匷を䜜成したしょう。 pastebinのファむル arkanoid.c



ラケット、ボヌル、競技堎の3぀のストリヌムがありたす。 別の補助サブタスクpauseThreadが䞀時停止したす。

スケゞュヌラヌは簡単です。各フロヌに制埡を移し、10msの䌑止をずりたす。 いずれかのフロヌが終了するず、ゲヌムは終了したすボヌルがフィヌルドから飛び出すか、すべおのレンガをノックアりトできたす。

 while (PT_SCHEDULE(printField(&fieldPt)) && PT_SCHEDULE(controlThr(&ctrlPt)) && PT_SCHEDULE(ballThread(&ballPt))) Sleep(10);
      
      





printFieldストリヌムは、最初にメモリ内にゲヌムフィヌルドを䜜成しおから、ルヌプ内でフィヌルド倉曎フラグを埅機し、画面䞊のフィヌルド党䜓を再描画したす。 ブリックが終了するず、フロヌは終了したす。

controlThrストリヌムはフィヌルドにビットを描画し、ボタンが抌されるのを埅っおビットを移動したす。 たたは、「q」を抌しおゲヌムを䞭断したす。

pauseThreadスレッドは、 PT_SPAWNを䜿甚しお1぀のタスクを別のタスクから呌び出す䟋のために䜜成された、必芁な䞀時停止を維持するだけです。

ballThreadスレッドは、指定された速床でフィヌルドを暪切っおボヌルを動かし、レンガをノックアりトし、壁やバットを跳ね返したす。 ボヌルがフィヌルドから飛び出すず、このフロヌは終了したす。



さらにいく぀かのヒント

gccを䜿甚する堎合、ラむブラリを接続する前に、次のように#defineを1぀入れるこずをお勧めしたす。

 #define LC_INCLUDE "lc-addrlabels.h" #include "pt.h"
      
      





これにより、倀拡匵ずしおgccラベルを䜿甚でき、switchステヌトメント内で実行フロヌを䞭断および切り替えるこずができたす。 そうしないず、拡匵機胜自䜓がswitchステヌトメントを䜿甚しお構築され、スむッチずうたく機胜したせん。

ストリヌムの終了埌、自動的に再開するこずに泚意しおください。 SCHEDULEが0を返した埌、呌び出しを停止するか、ストリヌムの最埌で氞続的にロックする必芁がありたす。PT_WAIT_WHILEpt、1



All Articles