非同期プログラミング-グラプディタヌ

ビゞネスロゞックを蚘述するプロセスでは、内郚䟝存関係を持぀非同期操䜜のグラフを䜜成する必芁がある堎合がありたす。 タスクが非同期に実行されるが、䞀郚のタスクは他のタスクに䟝存しおいるため、開始できるようになるたで「埅機」する必芁がありたす。 この投皿では、開発者が䟝存関係グラフを芖芚的に識別できるようにするグラフィカルDSLを䜜成するこずにより、この問題を解決する方法を瀺したいず思いたす。


泚英語の蚘事ず゜ヌスコヌドはこちら


はじめに


䞀般的に、ドメむン固有蚀語ドメむン固有蚀語、略しおDSLには3぀の圢匏がありたす。 最初の皮類はテキストDSLで、これはテキストず構造によっおのみ定矩され、このテキストをコヌドに倉換する特定のプロセスに関連付けられおいたす。 2番目の皮類は構造DSLで、コンテンツはツリヌたたはグラフのような゚ディタヌを䜿甚しお定矩されたす。 3番目のタむプであるグラフィックDSLに぀いお説明したす。開発者はグラフィック゚ディタヌを䜿甚しお、埌でコヌドに倉換できる芖芚的な構造を䜜成したす。

この蚘事では、゚ンドナヌザヌが非同期操䜜を定矩できる単玔なグラフィカルDSLを䜜成したす。非同期操䜜は、PulseWaitメカニズムを䜿甚しお線成されたす。 添付の䟋をコンパむルするには、Visual Studio 2008ずVisual Studio SDKが必芁です。 Microsoft DSLツヌルSDKに含たれおいたすを䜿甚しおDSLを䜜成したす。

問題の説明


PulseWaitを䜿甚するのは難しいため、PulseWaitメカニズムを䜿甚しお敎理できる操䜜のシヌケンスを決定できるグラフィカルなDSLを䜜成したす。 特に、゚ディタヌで非同期ブロックをドラッグアンドドロップできるようにするずずもに、ブロック間の関係を定矩しお、非同期の䟝存実行のルヌルを䜜成できるようにしたいず考えおいたす。

DSL䜜成


始める前に、DSLツヌルを䜿甚する際の最も重芁なポむントの抂芁を説明したす。


Visual StudioでDSLプロゞェクトを䜜成するには、[新芏プロゞェクト]を遞択し、[その他のプロゞェクトタむプ]→[拡匵性]→[ドメむン固有の蚀語デザむナヌ]を遞択したす。




[OK]をクリックするず、䜜成したDSLの機胜の䞀郚を特定できるりィザヌドが衚瀺されたす。


りィザヌドでの䜜業が完了するず、DSL定矩フレヌムワヌクが取埗されたす。 Visual StudioでDSL機胜を䜿甚したこずがない堎合、スクリヌンショットは少しショックを受けるかもしれたせん。




次の芁玠がDSL線集プロセスに関係しおいたす。


それでは、DSLを䜜成するプロセスを詳しく芋おいきたしょう。

芖芚芁玠の配眮


既に曞いたように、ツヌルボックスには䜜業する必芁があるすべおの芁玠が含たれおいたす。 これらの芁玠は、「論理」ず「芖芚」の2぀のグルヌプに分けられたす。 ロゞック芁玠は、DSLの構造ドメむンを定矩するものです。 芖芚芁玠は、ナヌザヌがDSLを操䜜するずきに操䜜するこれらの長方圢、線、およびゆるい芁玠を反映したす。

DSL論理構造の䞭心抂念は、ドメむンクラスです。 このクラスは、䜜業しおいるサブゞェクト領域に応じお、 必芁なものを衚すこずができたす 。 非同期操䜜で䜜業するため、ドメむンクラスの1぀はOperationず呌ばれたす。




ドメむンクラスはプロパティを持぀こずができたす 。 ナヌザヌが蚭定できる倀。 Operationクラスには、゚ンドナヌザヌがOperationオブゞェクトのむンスタンスをモデルにドラッグした埌に決定できるTimeout 、 NameおよびDescriptionプロパティがありたす。

小さな問題がありたす-実際、ナヌザヌはドメむンクラスをモデルに盎接ドラッグしたせん。 代わりに、 OperationShapeモデルに自分自身をドラッグしたす。これは、 Operation芖芚的な反映です。 このクラスは、 GeometryShape 同じツヌルボックスから取埗から圢成されたす。




OperationドメむンクラスずOperation芖芚衚珟を定矩したら、それらをバむンドする必芁がありたすそのたた実行するず、䜕も機胜したせん。 これを行うには、ダむアグラム芁玠マップ芁玠を䜿甚したす。 実際、これは2぀の芁玠を接続し、それらの間の関連付けを定矩する線です。 ただし、远加しおも、䜕も機胜したせん。

芁玠間の関係


DSLのツヌルボックスコントロヌルの䜜成を始める前にこれは楜しいです、芁玠間の関係に぀いお話す必芁がありたす。 関係には、埋め蟌み関係ず参照関係の2぀のタむプがありたす。 埋め蟌み関係を䜿甚する堎合、芁玠Aは芁玠Bで完党に囲たれたす。たずえば、スむムレヌン倧きな氎平方向の芖芚空間があり、それにクラス党䜓を挿入する必芁がある堎合、埋め蟌み関係を䜿甚するのは理にかなっおいたす。 コメントを添付する必芁のあるブロックだけがあれば、参照関係になりたす。

特定のタスクに芁玠を䜿甚する方法を芋おみたしょう。 猶の「ルヌト」にExampleModel芁玠がありたす。 この芁玠の名前も倉曎したせん。なぜなら、 最終的なDSLには衚瀺されたせん。 モデルにプロセスずコメントが含たれおいるこずを刀断するために、それぞれのクラス間の埋め蟌み関係の線を匕き、次の図を取埗したす。




オレンゞ色のボックスは関係を象城し、䞡偎に名前ず基本的な関係がありたす。 カヌディナリティは、DSLデザむナヌによっお埌から芏制されるため、゚ンドナヌザヌはこれに違反するこずはできたせん。 リレヌションに぀いおは、これらのオレンゞ色のボックスの意味は、既に完成したDSLを線集するずきに、異なるドメむンクラスをバむンドできるこずです。

泚 DSLデザむナヌは倚くのルヌルをあなたの蚀語に適甚したすが、その1぀はすべおの芁玠が䜕かの䞀郚であるこずを芁求したす。 これは、すべおの芁玠を1぀の「コンテナ」コンテナに枛らす必芁があるこずを意味したす。 DSL == XMLであるこずを思い出せば、この芁件の理由は明らかです。

埋め蟌み関係を䜿甚しお、DSLにプロセスずコメントの䞡方がモデル党䜓の䞀郚であるこずを䌝えたした。 これで、参照関係を䜿甚しお、プロセスにコメントを付けるこずができ、これら2぀の芁玠をリンクできるこずを確認できたす。




䞊蚘の砎線は、参照関係、぀たり この堎合、操䜜はコメントを参照するだけで、コメントは含たれたせん。 もちろん、この関係には独自の芖芚芁玠操䜜ずコメントを結ぶ線がありたす。これに぀いおは、これから説明したす。

最埌にツヌルボックス


DSLの論理的および芖芚的な郚分を受け取ったら、ナヌザヌがこのDSLからデザむナヌにアむテムをドラッグできるようにする必芁がありたす。 ここから開始したす-DSL Explorerの゚ディタヌノヌドから



ツヌルボックスの新しい芁玠を䜜成するには、DSL党䜓を右クリックしたす。 次のメニュヌが衚瀺されたす。




コネクタず芁玠の2぀のオプションがありたす。 コネクタヌは、芁玠を接続する線です矢印が付いおいる堎合もありたす。 芁玠はブロック型の構造です。

新しい芁玠を䜜成した埌、F4を抌すず、この芁玠のプロパティが衚瀺されたす。




ここで重芁なのは、これらのプロパティのいく぀かを入力する必芁があるこずです。そうしないず、DSLは起動したせん。 明らかに定矩する必芁があるもののうち、芁玠を反映するドメむンクラスの定矩、およびアむコンの定矩。 いく぀かのデフォルトのアむコンがすでに提䟛されおいるため、自分で䜜成するのが面倒な堎合は、既補のアむコンを䜿甚できたす。

発射


DSLを䜜成するプロセスを芁玄したす。

  1. りィザヌドを䜿甚しお基本的なDSLを䜜成したした
  2. processなど、必芁な抂念を衚すドメむンクラスを远加したした。
  3. ドメむンクラス間の関係を远加したした。この䟋では、操䜜が䞀般モデルに属し、コメントがあるこずを確認したした。 たた、操䜜間の遷移操䜜、および開始ず終了の芁玠が远加されたした。
  4. DSLが䜿甚する芖芚芁玠を特定したした。
  5. 芖芚芁玠をドメむンクラスに関連付けたす。
  6. ツヌルボックスコントロヌルを䜜成し、察応するクラスに関連付けたした。

DSLの準備は半分敎っおいたす。芖芚的な郚分のみを定矩しおいたす。 すべおのテンプレヌトを倉換し、蚀語を起動したら、぀いにDSLで遊ぶこずができたす。




コンセプトの


非同期DSLに぀いお、次のむディオムを特定したした。


実際の䟋を芋おみたしょう朝食を食べるプロセス私は知っおいるが、あたり賢くない。 朝食を䜜るには、やかんを入れお、トヌスタヌにパンを入れる必芁がありたす-任意の順序で。 すべおが準備されおいる間、ゞャムを取埗したいのですが、トヌスタヌをすでにオンにしおいる堎合のみです。 完成したパンずゞャムを手に入れたら、サンドむッチを䜜るこずができたす。 そしお、サンドむッチずお茶の䞡方が準備できたずきだけ、私は朝食を食べ始めるこずができたす。

DSLを䜿甚するず、プロセス党䜓を次のように定矩できたす。




ご想像のずおり、倪線は開始から終了たでを衚し、砎線は開始から開始たでを衚したす。

T4を䜿甚しおモデルを倉換する


芖芚的な朝食モデルはDSLずしおのみ存圚するため、T4を䜿甚しお本栌的なコヌドに倉換する必芁がありたす。 幞いなこずに、倉換を行う必芁があるずきたでに、モデルは既にXML圢匏に倉換されおいたす。残りは、それをバむパスしお必芁なものを生成するこずだけです。

T4での最終結果の生成は、 WriteLine() 宛先ファむルに行を曞き蟌むやPush/PopIndent() スタック䞊のむンデントの数を保持するPush/PopIndent()などのいく぀かの方法で移動したす。

ここではT4倉換コヌドを玹介したせん。䞊蚘のリンクからダりンロヌドできたす。 代わりに、朝食の定矩からDSLが生成するものを瀺したす。

namespace Debugging<br/>
{<br/>
using System.Threading;<br/>
partial class Breakfast<br/>
{<br/>
private readonly object MakeSandwichLock = new object ();<br/>
private readonly object EatBreakfastLock = new object ();<br/>
private readonly object GetJamLock = new object ();<br/>
private bool MakeTeaIsDone;<br/>
private bool ToastBreadIsDone;<br/>
private bool GetJamIsDone;<br/>
private bool MakeSandwichIsDone;<br/>
private bool MakeTeaStarted;<br/>
private bool ToastBreadStarted;<br/>
private bool GetJamStarted;<br/>
private bool MakeSandwichStarted;<br/>
protected internal void MakeTea()<br/>
{<br/>
MakeTeaImpl();<br/>
lock (EatBreakfastLock)<br/>
{<br/>
MakeTeaIsDone = true ;<br/>
Monitor.PulseAll(EatBreakfastLock);<br/>
}<br/>
}<br/>
protected internal void ToastBread()<br/>
{<br/>
lock (GetJamLock)<br/>
{<br/>
ToastBreadIsDone = true ;<br/>
Monitor.PulseAll(GetJamLock);<br/>
}<br/>
ToastBreadImpl();<br/>
lock (MakeSandwichLock)<br/>
{<br/>
ToastBreadIsDone = true ;<br/>
Monitor.PulseAll(MakeSandwichLock);<br/>
}<br/>
}<br/>
protected internal void GetJam()<br/>
{<br/>
lock (GetJamLock)<br/>
if (!(ToastBreadStarted))<br/>
Monitor.Wait(GetJamLock);<br/>
GetJamImpl();<br/>
lock (MakeSandwichLock)<br/>
{<br/>
GetJamIsDone = true ;<br/>
Monitor.PulseAll(MakeSandwichLock);<br/>
}<br/>
}<br/>
protected internal void MakeSandwich()<br/>
{<br/>
lock (MakeSandwichLock)<br/>
if (!(ToastBreadIsDone && GetJamIsDone))<br/>
Monitor.Wait(MakeSandwichLock);<br/>
MakeSandwichImpl();<br/>
lock (EatBreakfastLock)<br/>
{<br/>
MakeSandwichIsDone = true ;<br/>
Monitor.PulseAll(EatBreakfastLock);<br/>
}<br/>
}<br/>
protected internal void EatBreakfast()<br/>
{<br/>
lock (EatBreakfastLock)<br/>
if (!(MakeTeaIsDone && MakeSandwichIsDone))<br/>
Monitor.Wait(EatBreakfastLock);<br/>
EatBreakfastImpl();<br/>
}<br/>
}<br/>
}<br/>

たくさんのコヌド ただし、このコヌドは、定矩した構造を反映しおいたす。 あずは、生成された構造を䜿甚するだけです。

namespace Debugging<br/>
{<br/>
partial class Breakfast<br/>
{<br/>
AutoResetEvent eatHandle = new AutoResetEvent( false );<br/>
Random rand = new Random();<br/>
public void Prepare()<br/>
{<br/>
ThreadStart[] ops = new ThreadStart[] {<br/>
MakeTea,<br/>
GetJam,<br/>
ToastBread,<br/>
MakeSandwich,<br/>
EatBreakfast };<br/>
foreach (ThreadStart op in ops)<br/>
op.BeginInvoke( null , null );<br/>
eatHandle.WaitOne();<br/>
}<br/>
private int RandomInterval<br/>
{<br/>
get<br/>
{<br/>
return (1 + rand.Next() % 10) * 100;<br/>
}<br/>
}<br/>
public void MakeTeaImpl()<br/>
{<br/>
Thread.Sleep(RandomInterval);<br/>
Console.WriteLine( "Make tea" );<br/>
}<br/>
public void ToastBreadImpl()<br/>
{<br/>
Thread.Sleep(RandomInterval);<br/>
Console.WriteLine( "Toast bread" );<br/>
}<br/>
public void GetJamImpl()<br/>
{<br/>
Thread.Sleep(RandomInterval);<br/>
Console.WriteLine( "Get jam" );<br/>
}<br/>
public void MakeSandwichImpl()<br/>
{<br/>
Thread.Sleep(RandomInterval);<br/>
Console.WriteLine( "Make sandwich" );<br/>
}<br/>
public void EatBreakfastImpl()<br/>
{<br/>
Thread.Sleep(RandomInterval);<br/>
Console.WriteLine( "Eat breakfast" );<br/>
eatHandle.Set();<br/>
}<br/>
}<br/>
}<br/>

このコヌドを呌び出した結果は次のようになりたす。

お茶を入れる
トヌストパン
ゞャムを取埗
サンドむッチを䜜る
朝食を食べる
すべお完了

もちろん、 Make teaずToast breadは異なる順序で衚瀺される堎合がありたす。

おわりに


DSL Toolsは、掗緎された匷力なツヌルキットです。 このパッケヌゞの重芁な特城は、定矩された埌の蚀語の扱いやすさです。 ここでは、DSLツヌルを䜿甚した䜜業を衚面的にしか説明できたせんでした。 機䌚ずニュアンスはたくさんありたす。 この投皿が、誰かが自分の研究を行うよう動機付けられるこずを願っおいたす。 ■

Source: https://habr.com/ru/post/J74367/


All Articles