タスクローカル変数を使用する
タスクローカル変数はサイクル整合性があります。これらは、タスクサイクル内の1つの定義されたタスクによってのみ書き込まれますが、他のすべてのタスクはそれらに読み取りアクセスすることしかできません。タスクが他のタスクによって中断されたり、同時に実行されたりする可能性があることを考慮に入れています。サイクルの一貫性は、アプリケーションがマルチコアプロセッサを搭載したシステムで実行されている場合にも特に当てはまります。
したがって、タスクローカルグローバル変数リストを使用すると、複数のタスクが同じ変数を編集するときに、(コンパイラによって)自動的に同期を取得できます。これは、通常のGVLを使用する場合には当てはまりません。複数のタスクが、1サイクル中に通常のGVL変数に同時に書き込むことができます。
ただし、タスクローカル変数の同期は比較的時間がかかり、メモリを大量に消費するため、すべてのアプリケーションで適切な手段ではないことに注意してください。決定に役立つ詳細な技術情報とベストプラクティスのアドバイスについては、以下を参照してください。
の中に CODESYS プロジェクト、 グローバル変数リスト (tasklocal) オブジェクトは、タスクローカル変数を定義するために使用できます。構文的には、通常の GVL に対応しますが、変数への書き込みアクセス権を持つタスクの情報も含まれています。そのため、このような GVL 内のすべての変数は、タスクのサイクル中に別のタスクによって変更されることはありません。
ヒント
タスクローカル変数は、オンラインモードでは値を変更できない複雑な変数です。 値を書き込む 指示。
次のセクションでは、タスクローカル変数の原理と機能を示す簡単な例を示します。書き込みプログラムと読み取りプログラムがあります。プログラムは異なるタスクで実行されますが、タスクローカルグローバル変数リストにある同じデータにアクセスして、サイクル一貫して処理されるようにします。
例に示されている機能
このサンプルアプリケーションを再プログラミングする手順は、以下にあります。
(* task-local GVL, object name: "Tasklocals" *) VAR_GLOBAL g_diaData : ARRAY [0..99] OF DINT; END_VAR PROGRAM ReadData VAR diIndex : DINT; bTest : BOOL; diValue : DINT; END_VAR bTest := TRUE; diValue := TaskLocals.g_diaData[0]; FOR diIndex := 0 TO 99 DO bTest := bTest AND (diValue = Tasklocals.g_diaData[diIndex]); END_FOR PROGRAM WriteData VAR diIndex : DINT; diCounter : DINT; END_VAR diCounter := diCounter + 1; FOR diIndex := 0 TO 99 DO Tasklocals.g_diaData[diIndex] := diCounter; END_FOR
プログラム WriteData と ReadData さまざまなタスクによって呼び出されます。
プログラム中 WriteData
配列になります g_diaData
値でいっぱい。プログラム ReadData
配列の値が期待どおりかどうかをテストします。この場合、変数は次のようになります。 bTest
結果として TRUE
。
テストされている配列データは変数に関するものです g_diaData
オブジェクト内 Tasklocals
タイプの Globale Variablenliste (tasklokal)
宣言した。これにより、コンパイラーでのデータアクセスが同期され、アクセスするプログラムが異なるタスクから呼び出された場合でも、データはサイクル整合性が保証されます。サンプルプログラムでは、これは具体的には変数が test
プログラムで ReadData
いつも TRUE
は。
変数の場合 g_diaData
この例では、グローバル変数リストとしてのみ宣言されています。テスト、つまり変数 test
プログラムで ReadData
、より頻繁に FALSE
配達。この場合、 FOR
-ループは他のタスクによって中断される可能性があります。または、両方のタスクが同時に実行される可能性があります(マルチコアコントローラー)。したがって、リーダーがリストを読んでいる間に、ライターが値を変更する可能性があります。
宣言の制限
重要
タスクローカル変数のリストの宣言に変更が加えられた後は、アプリケーションをオンラインで変更することはできません。
グローバルタスクローカル変数リストを宣言するときは、次の点に注意してください。
AT宣言を介して直接アドレスを割り当てないでください。
PLC構成のローカルタスク変数にマップしないでください。
ポインタを宣言しないでください。
参照を宣言しないでください。
機能ブロックをインスタンス化しないでください。
タスクローカル変数を同時に宣言しないでください
PERSISTENT
とRETAIN
。
書き込み許可のないタスクでの書き込みアクセスは、コンパイラーによってエラーとして報告されます。ただし、書き込み権が侵害されている場所をすべて特定することはできません。コンパイラーは、静的呼び出しのみをタスクに割り当てることができます。ただし、たとえば、ポインタまたはインタフェースを介して機能ブロックを呼び出すことは、タスクに割り当てられません。これは、書き込みアクセスもそこに記録されないことを意味します。さらに、ポインターはタスクローカル変数を指すことができます。このようにして、データは読み取りタスクで操作できます。この場合、ランタイムエラーも発行されません。ただし、ポインタを介してアクセスするときに変更された値は、変数の共通参照にコピーされません。
タスクのプロパティ-ローカルグローバル変数と考えられる動作
変数は、異なるアドレスの各タスクのリストにあります。つまり、読み取りアクセスの場合: ADR(variable name)
タスクごとに異なるアドレスを返します。
同期メカニズムは、次のことを保証します。
サイクルの一貫性
ロック状態からの解放:タスクが別のタスクによるアクションを待機することはありません。
ただし、この方法を使用して、読み取りタスクが書き込みタスクのコピーを確実に受信する時点を決定することはできません。原則として、コピーは発散する可能性があります。上記の例では、書き込まれたすべてのコピーがリーダーによって一度編集されるとは限りません。たとえば、読み取りタスクは同じ配列を数サイクル処理したり、配列の内容は2サイクルの間で1つ以上の値を「スキップ」したりできます。両方が発生する可能性があり、考慮する必要があります。
書き込みタスクは、各読み取りタスクによる共通参照への2つのアクセス間の1サイクルの間保持できます。つまり、 n
読み取りタスクが存在し、書き込みタスク n
サイクルは、共有参照の次の更新まで遅延されます。
読み取りタスクは、書き込みタスクによって各サイクルで読み取りコピーを取得することを防ぐことができます。したがって、読み取りタスクが確実にコピーを受け取るまでの最大サイクル数を指定することはできません。
特に、実行速度が非常に遅いタスクが含まれる場合、これは問題になる可能性があります。タスクが1時間ごとにのみ実行され、タスクローカル変数にアクセスできないと仮定すると、タスクはリストの非常に古いコピーで動作しています。したがって、タスクローカル変数にタイムスタンプを挿入することは理にかなっています。これは、読み取りタスクが少なくともリストが最新であるかどうかを判断するために使用できます。次のようにタイムスタンプを追加できます。タスクローカル変数のリストにタイプの変数を追加します。 LTIME
たとえば、書き込みタスクでは、次のコードを使用します。 tasklocal.g_timestamp := LTIME();
。
ベストプラクティス
タスクローカル変数は、「単一のライター-複数のリーダー」のユースケース向けに設計されています。さまざまなタスクによって呼び出されるコードを実装する場合、タスクローカル変数を使用すると非常に便利です。たとえば、これは上記のサンプルアプリケーションの場合です。 appTasklocal
すべてが同じ配列にアクセスし、同じ関数を使用する複数の読み取りタスクによって拡張される場合。
タスクローカル変数は、マルチコアプロセッサを搭載したシステムで特に役立ちます。これらのシステムでは、優先度によってタスクを同期することはできません。次に、他の同期メカニズムが必要になります。
読み取りタスクが常に変数の最新のコピーで機能する必要がある場合は、タスクローカル変数を使用しないでください。ローカルタスク変数はこれには適していません。
同様の問題は「生産者-消費者」問題です。これは、タスクがデータを生成し、2番目のタスクがそれを処理する場合です。このコンステレーションでは、別のタイプの同期を優先します。たとえば、プロデューサーはフラグを使用して、新しい日付が利用可能であることを示すことができます。コンシューマーは、2番目のフラグを使用して、データを処理し、新しい入力を待機していることを示すことができます。どちらも同じデータで機能します。データの循環コピーのオーバーヘッドはなく、コンシューマーはプロデューサーによって生成されたデータを失うことはありません。
モニタリング
実行時に、メモリ内にタスクローカル変数リストのいくつかの、場合によっては異なるコピーがあります。ただし、位置を監視するときにすべての値を表示できるわけではありません。このため、共通参照からの値は、インラインモニタリング、モニタリングリスト、および視覚化でタスクローカル変数に表示されます。
ブレークポイントを設定すると、ブレークポイントに到達して停止したタスクのデータが表示されます。その間、他のタスクは実行を続けます。特定の状況下では、共通コピーを変更できます。ただし、停止したタスクのコンテキストでは、値は変更されないままであり、そのように表示されます。これに注意する必要があります。
背景:技術的な実装
タスクローカル変数のリストについては、コンパイラーは各タスクのコピーとすべてのタスクの共通参照コピーを作成します。タスクローカル変数のリストと同じ変数を含む構造が作成されます。この構造の配列も作成され、タスクごとに配列の次元が作成されます。したがって、配列要素はタスクごとにインデックスが付けられます。リストの変数がコードでアクセスされるようになった場合、リストのタスクローカルコピーが実際にアクセスされます。さらに、ブロックが現在実行されているタスクが判別され、それに応じてアクセスにインデックスが付けられます。
たとえば、コード行 diValue := TaskLocals.g_diaData[0];
上記の例から置き換えられました:
diValue := __TaskLocalVarsArray[__CURRENTTASK.TaskIndex].__g_diarr[0];
__CURRENTTASK
abの演算子です CODESYS V3.5 SP13は、現在のタスクインデックスをすばやく決定するために使用できます。
実行時に、タスクローカルリストのコンテンツは、書き込みタスクの最後に共通参照にコピーされます。読み取りタスクの場合、共通参照の内容は最初にタスクローカルコピーにコピーされます。したがって、n個のタスクのリストのn + 1個のコピーがあります。リストは共通の参照として機能し、さらに各タスクには独自のリストのコピーがあります。
スケジューラーは、複数のタスクのタイムリーな実行、つまりタスクの切り替えを制御します。実行時間の割り当てを制御するためにスケジューラーが従う戦略は、タスクのブロックを回避することを目的としています。したがって、同期メカニズムはタスクローカル変数のプロパティに対して最適化されているため、ブロッキング状態(ロック状態)が回避され、タスクは別のタスクのアクションを待機しません。
同期戦略:
書き込みタスクが共有参照にコピーを書き戻す限り、読み取りタスクはいずれもコピーを取得しません。
読み取りタスクが共有参照からコピーをフェッチする限り、書き込みタスクはコピーを書き戻しません。
上記のサンプルアプリケーションを作成するための手順
目的:プログラムから始めたい ReadData
プログラムがアクセスするのと同じデータにアクセスする WriteData
書かれる。 2つのプログラムは異なるタスクで実行する必要があります。データをタスクローカル変数リストに指定して、サイクル一貫して自動的に処理されるようにします。
要件:標準プロジェクトが新たに作成され、エディターで開かれます。
アプリケーションの名前を
Application
のappTasklocal
その周り。以下に追加
appTasklocal
名前によるSTのプログラムReadData
追加した。以下に追加
appTasklocal
名前によるSTの別のプログラムWriteData
追加した。デフォルトのタスクに名前を付ける
MainTask
オブジェクトの下Taskkonfiguration
のRead
その周り。対話を追加する 構成 タスク
Read
ボタン経由 通話を追加 プログラムを呼び出すReadData
追加した。オブジェクトの下に貼り付けます タスク構成 で名前が付けられた別のタスクを追加しました
Write
このタスクにプログラム呼び出しを追加しますWrite
追加した。タスク構成に2つのタスクがあります
Write
とRead
誰がプログラムWriteData
それぞれReadData
電話。アプリケーションを選択します
appTasklocal
タイプのオブジェクトを追加します グローバル変数リスト(タスクローカル) 追加した。対話 グローバル変数リスト(タスクローカル)を追加します。 開きます。
名前として入力
Tasklocals
1。ドロップダウンリストから選択します 書き込みアクセス権のあるタスク タスク
Write
。アプリケーション内でローカルタスク変数を使用するためのオブジェクト構造が完成しました。これで、上記の例の説明に示されているようにオブジェクトをコーディングできます。