Open15

Verse Concurrency—Time Flow: Everything, Everywhere in UEFN, All at Once | Unreal Fest 2023について要約する

イワケンイワケン

日本語要約

このプレゼンテーションは、Unreal Editorを使用したFortniteの開発と、Verseというゲームプログラミング言語との関連性について説明しています。Verseは、時間の流れを扱う概念を導入し、ゲーム開発のプロセスをより直感的で理解しやすいものにしています。これは、伝統的なゲーム開発が一瞬の時間を詳細に記述するのに対し、Verseでは時間の流れを記述することで、ゲームの進行に合わせて異なる事象が発生することを可能にしています。

Verseでは、同時に発生する事象を管理するための独自の方法があります。これらは、ブロック、同期、レース、ラッシュ、ブランチの5つのコマンドによって制御されます。ブロックは、一連のコマンドを順番に実行します。同期は、すべてのコマンドが完了するまで次のコマンドに移動しないようにします。レースは、最初に完了したコマンドが勝者となり、他のすべてのコマンドがキャンセルされます。ラッシュはレースと似ていますが、最初に完了したコマンドが次のコマンドに移動した後も、他のコマンドは続行します。ブランチは、各コマンドを順番に実行しますが、次のコマンドにすぐに移動します。
これらのコマンドはすべて、非同期関数のスコープ内に留まります。つまり、関数が終了すると、それらのコマンドもキャンセルされます。ただし、スポーンというコマンドは例外で、関数のスコープを超えて実行されます。これは、一連のタスクを作成し、それらが完了するのを待つことができます。
このシステムは、時間の流れを管理するための直感的な方法を提供し、複雑なゲーム開発のタスクをシンプルにすることができます。

Verseプログラミングでよく見かけるコマンドの一つに「sleep」があります。このコマンドは、渡される数値によって基本的に異なるモードを持ちます。0未満の数値を渡すと、すぐに次の行に移動します。0を渡すと、サーバーの更新を1回待ちます。これは約1/30秒です。しかし、これはクライアントの更新速度ではなく、サーバーの更新速度に基づいています。無限に待つ場合、つまり「sleep」コマンドを使って、プログラムが永遠に返らないようにすることも可能です。
また、「event」クラスを使って、異なる時間フロータスク間でイベントシグナリングを行うことができます。これにより、イベントを作成し、それを多くの場所で待つことができます。そして、イベントがシグナルされると、そのイベントを待っているすべてのものが起きて次の表現に進みます。
Verseプログラミングの主要なエントリーポイントは、レベル内に配置したクリエイティブデバイスの「on begin」であり、他のAPI内の任意の「suspends」関数も含まれます。
これらの要素を組み合わせることで、「ステージベースのプログラミング」が実現します。これは、Verseでの事象をステージディレクションのように扱うことを指します。例えば、複雑なタスクを実行している最中に何か他の事象が発生した場合、通常はその複雑なタスクを停止するためのフックを設定する必要があります。しかし、Verseでは、そのようなフックを設定する必要はありません。代わりに、「レース」コマンドを使用して、一連のタスクと並行して別の事象(例えば、火山の噴火)を実行することができます。そして、その事象が最初に完了した場合、他の全てのタスクはキャンセルされます。
また、関数の引数として非同期コールを渡すと、それらは順番に実行されます。しかし、それらをすべて同時に実行したい場合は、「sync」を使用してグループ化することができます。これにより、すべてのタスクが同時に実行され、全てが完了した時点で次の処理に移ることができます。

Verseプログラミングでは、「defer」コマンドがあります。これは、スコープを終了するときに呼び出されるため、クラスやオブジェクトのデストラクタの役割を果たします。これにより、非同期関数がキャンセルされた場合でも、必要なクリーンアップを行うことができます。
また、非同期関数内でローカル変数を作成すると、そのスコープ内の他の非同期コールすべてがその変数にアクセスできます。これにより、異なる非同期タスク間で変数を共有し、通信することが可能になります。
さらに、非同期プログラミングでは、特定のタスクを繰り返すことがよくあります。これは通常、無限ループを作成することで実現します。しかし、'race'コマンドを使用すると、無限ループから抜け出すことができます。これは、'race'コマンドが他の実行中のタスクをキャンセルするためです。
また、異なる非同期コールを行うときには、しばしば時間をかけて作業を分散することがあります。例えば、ゲーム内のプレイヤーに対して複雑なテストを行う場合、プレイヤーの数に基づいてテストの間隔を調整することができます。これにより、プレイヤーの数に関係なく、常に同じ数のテストを行うことができます。
これらの要素を組み合わせて使用すると、より強力なプログラミングが可能になります。例えば、イベントベースの非同期タスクをループ内で待機させると、最も時間のかかるタスクが終了するまでループが待機することになります。しかし、各タスクが異なる時間を必要とする場合、それらを同じループ内に置くのではなく、各タスクを別々のループ内で待機させると効率的です。

「defer」コマンドは、スコープを終了するときに呼び出されるため、非同期関数がキャンセルされた場合でもクリーンアップを行うことができます。これは、複雑な動作がキャンセルされた場合に何か特定の動作を行いたいときに役立ちます。
非同期関数内でローカル変数を作成すると、そのスコープ内の他の非同期コールがその変数にアクセスできます。これは、同時に実行される異なるタスク間で変数を共有し、通信することを可能にします。
無限ループは通常、プログラミングにおいては避けるべきですが、非同期プログラミングでは、特定のタスクを何度も繰り返すために無限ループを使用することがあります。しかし、'race'コマンドを使用すると、無限ループから抜け出し、他のタスクをキャンセルすることができます。
また、複数の非同期タスクを順次ではなく、並行して実行したい場合には、それらを'race'コマンドでラップすることが推奨されます。これにより、いずれか一つのタスクが完了した時点で、他のタスクすべてがキャンセルされ、次の処理に進みます。
一方、'subscribe'コマンドを使用すると、特定のイベントが発生するたびに何かを実行することができますが、'subscribe'コマンドは一度実行すると繰り返し呼び出されるため、一度だけ実行したい場合には不適切です。その代わりに'await'コマンドを使用することで、一度だけ実行することができます。
また、ゲームのロジックを作成する際には、まず全体のフローを大まかに考え、それを基に各部分の詳細を埋めていくと効率的です。これにより、ゲームのロジック全体が一つの大きなループとして表現され、その中に各部分の処理が含まれる形になります。これにより、全体のフローを把握しやすくなり、ロジックの改善も容易になります。

イワケンイワケン

  • Traditional Game Dev
    • Based on tick / update
    • Describes a single instant of time
    • State machines & case statements
    • Hard keeping state across updates
    • Unintuitive
  • Verse Game Dev
    • Based on time flow
    • Stage based passage of time
    • Time logic is state
    • Local variables as persistent state
    • How the human mind works

日本語訳

このスライドは、伝統的なゲーム開発とVerseゲーム開発の主な違いを説明しています。

  • 伝統的なゲーム開発(Traditional Game Dev):
    • ティック/アップデートに基づいています。これは、ゲームの状態が一定の間隔(ティック)で更新されるという意味です。
    • 単一の瞬間の状態を記述します。つまり、各アップデートはゲームの特定の瞬間を表します。
    • 状態マシンとcase文を使用します。これらは、ゲームの異なる状態とその間の遷移を管理するためのツールです。
    • アップデート間での状態の維持が難しいです。これは、各アップデートが独立しているため、一つのアップデートから次へと状態を引き継ぐのが難しいという意味です。
    • 直感的ではありません。これは、状態の管理と時間の流れをコードで表現するのが難しいためです。
  • Verseゲーム開発(Verse Game Dev):
    • 時間の流れに基づいています。これは、ゲームの状態が時間とともに連続的に変化するという意味です。
    • 時間の経過に基づいたステージを記述します。つまり、ゲームの進行は一連のステージ(またはイベント)によって表現されます。
    • 時間のロジックが状態です。これは、ゲームの状態が時間とともに進行するイベントの結果として定義されるという意味です。
    • ローカル変数を持続的な状態として使用します。これにより、一つのイベントから次へと状態を維持することが容易になります。
    • 人間の思考の仕方と一致しています。これは、時間の流れとイベントの進行という概念が、人間の経験と直感とより一致しているという意味です。
      つまり、伝統的なゲーム開発とVerseゲーム開発の主な違いは、時間の扱い方と状態の管理方法にあります。Verseは時間の流れとイベントの進行に基づいてゲームを表現することで、より直感的で効率的なゲーム開発を可能にしています。
イワケンイワケン

Timeflow expressions

  • immediate / async
  • Time flow as event-based / polling / continuous work / combination
  • Imediate expressions are grouped as single atomic unit

Functions over time

  • Async function / coroutine
    • Define function with <suspends>
    • Call just like any function:
      • Coro(arg1,arg2)

日本語訳

  • 時間の流れの表現(Timeflow expressions):
    • 即時/非同期: これは、表現(コード)がすぐに実行されるか(即時)、それとも後で実行されるか(非同期)を示します。
    • イベントベース、ポーリング、連続的な作業、またはその組み合わせとしての時間の流れ: これは、時間の経過をどのように表現するかを示します。イベントベースは特定のイベントが発生したときに反応する方法、ポーリングは定期的に状態をチェックする方法、連続的な作業は一定のタスクを続ける方法を指します。
    • 即時の表現は単一の原子単位としてグループ化されます: これは、即時の表現(すぐに実行されるコード)は一つの単位として扱われ、それらが一緒に実行されることを示します。
  • 時間を通じての関数(Functions over time):
    • 非同期関数/コルーチン: これは、時間を通じて実行される関数を定義する方法を示します。非同期関数またはコルーチンは、一部の処理を一時停止し、後で再開することができます。
    • 関数は<suspends>を用いて定義されます: これは、非同期関数を定義するための特殊なキーワードで、この関数が一時停止可能であることを示します。
    • 関数は通常の関数と同様に呼び出されます: つまり、非同期関数も通常の関数と同様に、引数を与えて呼び出すことができます。
      つまり、このスライドでは、時間の流れをどのように表現するか、そして時間を通じてどのように関数を実行するかについて説明しています。これらの概念は、ゲーム開発や他の非同期プログラミングのコンテキストで非常に重要です。
イワケンイワケン

The good stuff

Async expressions -lifetime constrained to async context / coroutine body

日本語要約

このスライドは、非同期表現(Async expressions)について述べています。特に、その寿命が非同期コンテキストまたはコルーチンの本体に制約されるという特性に焦点を当てています。
非同期表現は、非同期コンテキスト(たとえば、非同期関数またはコルーチン)内でのみ存在し、実行されます。これは、非同期表現が一時停止したり、後で再開したりする能力を持つため、非同期コンテキスト内でしか正しく機能しないからです。
非同期コンテキストまたはコルーチンの本体が終了すると、そのコンテキスト内のすべての非同期表現も終了します。これは、非同期表現の「寿命」がその非同期コンテキストに「制約される」ことを意味します。
この特性は、非同期プログラミングにおいて重要です。なぜなら、これにより開発者は非同期表現がいつ、どこで、どのように実行されるかを正確に理解し、制御することができるからです。これは、コードの読みやすさ、デバッグの容易さ、そして全体的なコードの品質を向上させます。

イワケンイワケン

Time Logic

sync is and logic

  • Once all async subexpressions have completed then the next expression is executed
  • If any of the subexpressions do not complete then next expression does not run

race is or logic

  • One any async eubexpression has completed then
    • other sibling subexpressions are cancelled; and
    • next expression is executed

日本語要約

このスライドでは、非同期プログラミングにおける"同期(sync)"と"レース(race)"という二つの重要な概念について説明しています。これらは非同期サブ表現(つまり、非同期関数やコルーチン)がどのように実行され、それらが完了すると次に何が起こるかを決定します。

  • 同期はANDロジック(sync is and logic):
    • すべての非同期サブ表現が完了したら次の表現が実行されます。これは、非同期サブ表現が並列に実行され、すべてが完了するまで次のステップには進まないという意味です。これは論理的な"AND"に似ています(すべての条件が真である場合にのみ真と評価されます)。
    • サブ表現のいずれかが完了しない場合、次の表現は実行されません。これは、すべての非同期サブ表現が成功する必要があることを意味します。もし一つでも失敗または完了しなければ、全体の表現は次に進みません。
  • レースはORロジック(race is or logic):
    • いずれかの非同期サブ表現が完了したら、その他の同じレベルのサブ表現はキャンセルされ、次の表現が実行されます。これは、非同期サブ表現が並列に実行され、最初に完了したものが全体の結果を決定するという意味です。これは論理的な"OR"に似ています(いずれかの条件が真であれば真と評価されます)。
      これらの概念は、非同期プログラミングにおいて非常に重要であり、特に複数の非同期タスクを効率的に管理するために使用されます。
イワケンイワケン

THe escape hatch

spawn can be called anywhere and has a unconstrained lifetime - akin to goto

Use as an escape hatch or building block and prefer structured concurrency.

task class - result of spawn:

MyTask := spawn{Coro()}

  • Used to Await() a coroutine
    • potentially at multipe call points
    • Any call point can also get a result:
    • Result := MyTask.Await()

日本語要約

このスライドは、非同期プログラミングにおける spawn という概念と、その使用方法について説明しています。

  • spawn は、どこからでも呼び出すことができ、制約のない寿命を持つと説明されています。これは、非同期タスクを起動するための一種の"逃げ道"や基礎的な構成要素として使用されます。これは"goto"文に似ているとも言えますが、その使用は一般的には制御が難しくなるため、避けられるべきです。
  • その代わりに、"構造化された並行性"(structured concurrency)を好むべきです。これは、非同期タスクが明確なライフサイクルとスコープを持ち、それらが互いに関連付けられているというプログラミングパラダイムを指します。
  • spawn の結果はtask クラスとして表現されます。このクラスは、非同期タスクの状態を管理し、その結果を取得するためのメソッドを提供します。
  • 例えば、MyTask := spawn{Coro()}というコードは、"Coro"というコルーチンを非同期に実行し、その結果を"MyTask"というタスクオブジェクトに格納します。
  • そして、Result := MyTask.Await()というコードは、"MyTask"の結果が利用可能になるまで待機し、その結果を"Result"に格納します。この"Await()"メソッドは、複数の呼び出し箇所で使用することができます。
    つまり、このスライドでは非同期タスクの生成と管理について説明しています。これらの概念は、非同期プログラミングにおいて非常に重要であり、特に複数の非同期タスクを効率的に管理するために使用されます。
イワケンイワケン

Assorted toolbox

  • Subexpressions with blocks:
    • block, sync, race, rush, branch
    • loop, for, case
  • Simulation.Sleep():
    • < 0
      • immediate
    • 0.0
      • wait 1 server update [not client]
    • 0 < NUM < Inf
      • wait Num server seconds
    • Inf
      • wait forever
    • event parametric class
      • Signal()
      • Await()
    • Entry points on creative_device.OnBegin()
    • All <suspends> functions

日本語要約

このスライドは、非同期プログラミングにおけるさまざまなツールと概念について説明しています。

  • サブ表現とブロック: これらは非同期プログラミングにおける基本的な構造要素で、ブロック(block)、同期(sync)、レース(race)、ラッシュ(rush)、ブランチ(branch)、ループ(loop)、forループ、case文などがあります。これらの各要素は特定の非同期の動作を実装します。
  • Simulation.Sleep(): これは指定した時間だけプログラムの実行を一時停止するための関数です。引数によって挙動が変わります。
    • 引数が負の場合、即時に次の命令に進みます。
    • 引数が0.0の場合、1回のサーバーアップデートを待ちます(クライアントではなく)。
    • 引数が0より大きく無限小より小さい場合、指定した数値のサーバーセカンドを待ちます。
    • 引数が無限大(Inf)の場合、永遠に待ち続けます。
  • eventパラメトリッククラス: Signal()とAwait()というメソッドを提供します。これらは非同期イベントの発生と待機を制御します。
  • Entry points on creative_device.OnBegin(): これは特定のエントリーポイント(開始点)を示しています。プログラムはこのポイントから実行を開始します。
  • All <suspends> functions: これはすべてのサスペンド(一時停止)関数を示しています。これらの関数は非同期タスクの一時停止と再開を制御します。
    このスライドは、非同期プログラミングのさまざまなツールと概念を提供し、それらがどのように動作し、どのように使用されるかを説明しています。これらのツールは、非同期タスクの効率的な管理と制御に不可欠です。
イワケンイワケン

Race against a Volcano

  • Complex actions that occur over time just do their thing and do not need to worry about when to cancel
  • Use a race with those actions and any number of confitions that should cancel them and take over
race:
    ExploreAndEscapeIsland()
    block:
        VolcanoCountdown()
        EveryoneDies()

日本語訳

このスライドは、非同期プログラミングにおける"race"の概念を具体的なシナリオを通じて説明しています。このシナリオは「火山との競争」で、非同期タスクがどのように並行して実行され、どのようにキャンセルされるかを示しています。

  • "race"は、複数の非同期タスクを並行して実行し、最初に完了したタスクが全体の結果を決定します。他のタスクはキャンセルされます。これは、複数の可能性のある結果の中から一つだけ必要な場合や、複数のタスクが競争状態にある場合に有用です。
    このスライドのコード例では、"ExploreAndEscapeIsland()"という非同期タスクと、"block"内の"VolcanoCountdown()"と"EveryoneDies()"という二つの非同期タスクが競争状態にあります。
  • "ExploreAndEscapeIsland()"は、島の探索と脱出を試みるタスクを表しています。これは時間がかかる複雑な操作を含むかもしれません。
  • "VolcanoCountdown()"は、火山の噴火までのカウントダウンを表しています。これは一定の時間が経過すると完了します。
  • "EveryoneDies()"は、火山が噴火し全員が死亡するという結果を表しています。
    "race"ブロック内でこれらのタスクは並行して実行されます。"ExploreAndEscapeIsland()"が最初に完了すれば(つまり、全員が島から脱出する前に火山が噴火しなければ)、"block"内のタスクはキャンセルされます。逆に、"VolcanoCountdown()"が最初に完了すれば(つまり、全員が島から脱出する前に火山が噴火すれば)、"ExploreAndEscapeIsland()"はキャンセルされ、"EveryoneDies()"が実行されます。
    このように、"race"は複数の非同期タスクの中から最初に完了したものを選ぶことを可能にし、その結果に基づいて他のタスクをキャンセルします。これは非同期プログラミングにおいて非常に強力なツールで、特にタイムアウトや競争状態の管理に有用です。
イワケンイワケン

Using it all together

  • Mixing expressions - especially flow control
    • block,sync,race,
    • loop,if,for,case
    • spawn,rush,branch
    • sync tuple result as async arguments

日本語訳

このスライドでは、非同期プログラミングにおけるさまざまな表現やフロー制御の組み合わせについて説明しています。

  • ブロック(block)、同期(sync)、レース(race)などのフロー制御表現は、非同期タスクの実行順序やタスク間の依存関係を制御します。これらは一緒に使われることが多く、特定のタスクが完了するまで他のタスクを待機させたり、複数のタスクを並行して実行したりします。
  • ループ(loop)、if文、for文、case文などの制御構造は、プログラムのフローを制御します。これらは非同期タスクの実行においても重要で、特定の条件に応じて異なるタスクを実行したり、同じタスクを繰り返し実行したりします。
  • スポーン(spawn)、ラッシュ(rush)、ブランチ(branch)などの非同期表現は、新たな非同期タスクを生成し、それらを並行または非同期に実行します。これらは一緒に使われることが多く、非同期タスクの生成と管理を助けます。
  • 同期タプルの結果を非同期引数として使用するという表現は、非同期タスクの結果を他の非同期タスクの引数として使用することを可能にします。これにより、非同期タスク間でデータを簡単に共有することができます。
    これらのすべての要素を組み合わせることで、非常に複雑な非同期のフロー制御とデータ共有を実現することができます。これは非同期プログラミングにおける重要なスキルであり、これらの要素を適切に組み合わせることで、効率的で読みやすい非同期コードを書くことができます。
イワケンイワケン

# Resets all the props to their original positions
(Prop : creative_prop).Move2(Point1 : map_indicator_device, Point2 : map_indicator_device)<suspends>:map_indicator_device=
    PropA.MoveToPositional(Point1, offset0, Arrive1m)
    PropA.MoveToPositional(Point2, offset0, Arrive1m)
    Point2

AsyncArgsSerial()<suspends>:void=
    PropA.Move2(
        PropB.Move2(MapPointC, MapPointD),
        PropC.Move2(MapPointD, MapPointA))



AsyncArgsConcurrent()<suspends>:void=
    PropA.Move2(
        sync:
            PropB.Move2(MapPointC, MapPointD),
            PropC.Move2(MapPointD, MapPointA)
    )

日本語訳

このコードは、非同期プログラミングにおけるプロップ(Prop)の移動を制御するためのものです。具体的には、プロップを一つの位置から別の位置へ移動させる非同期タスクを定義しています。さらに、これらのタスクをシリアル(順序通り)にまたは並行(同時に)に実行するための関数も定義しています。

  • Move2関数は、指定したプロップを一つの位置(Point1)から別の位置(Point2)へ移動させるタスクを生成します。この関数は非同期に実行され、Point2を返します。MoveToPositionalメソッドは、プロップを指定した位置へ移動させるためのもので、オフセットと到着時間を引数に取ります。
  • AsyncArgsSerial関数は、複数のプロップの移動タスクをシリアルに実行します。つまり、一つのタスクが完了した後に次のタスクが開始されます。この関数はプロップAを移動させるタスクを生成し、その移動先はプロップBとプロップCの移動タスクの結果に依存します。
  • AsyncArgsConcurrent関数は、複数のプロップの移動タスクを並行に実行します。つまり、全てのタスクが同時に開始されます。この関数はプロップAを移動させるタスクを生成し、その移動先はプロップBとプロップCの移動タスクの結果に依存します。ただし、syncキーワードにより、プロップBとプロップCの移動タスクは同時に開始され、両方のタスクが完了した時点でプロップAの移動タスクが開始されます。
    このように、このコードは非同期プログラミングを利用して複数のプロップの移動を効率的に制御するための一例を示しています。非同期タスクの生成と管理、シリアルと並行の制御、タスク間のデータ共有など、非同期プログラミングの基本的な要素を活用しています
イワケンイワケン

Using it all together

  • Mixing expressions - especially flow control
    • block,sync,race,
    • loop,if,for,case
    • spawn,rush,branch
    • sync tuple result as async arguments
  • defer on cancel
  • Locals as persistent state
  • Never ending concurrent expressions esp. with raec (event driven or working over time)
  • Spreading work over time
    • doing nothing
    • Human-speed reactions

日本語訳

このスライドでは、非同期プログラミングのさまざまな要素とその組み合わせについて説明しています。具体的には以下のようなトピックが含まれています。

  • フロー制御の表現の混合:block、sync、raceなどの非同期制御構造を組み合わせることで、より複雑で柔軟な非同期フローを作成できます。同様に、loop、if、for、caseなどの制御構造も非同期プログラミングにおいて重要です。また、spawn、rush、branchなどの操作を使って新しい非同期タスクを生成し、それらを管理します。さらに、同期タプルの結果を非同期引数として使用することで、非同期タスク間でデータを簡単に共有できます。
  • キャンセル時のdefer:非同期タスクがキャンセルされたときに実行するクリーンアップ操作を指定できます。これはリソースの解放や状態のリセットなど、タスクの終了時に必要な操作を確実に行うための重要な機能です。
  • ローカル変数を持続的な状態として使用:非同期タスクが持続的な状態を必要とする場合、ローカル変数を使用してその状態を保持できます。これはタスク間でデータを共有するための一つの方法です。
  • 終わりのない並行表現:特にrace(競争)を使用すると、イベント駆動型のプログラミングや時間をかけて作業を行うような非同期タスクを表現できます。これは、非同期タスクが外部のイベントに応答するような場合や、タスクが長期間にわたって実行される必要がある場合に有用です。
  • 時間をかけて作業を行う:非同期タスクが「何もしない」時間を必要とする場合や、人間の反応速度に合わせてタスクを実行する必要がある場合には、時間をかけて作業を行うことができます。これは、タスクの実行を適切なタイミングにスケジュールするための重要な機能です。
    これらの要素を適切に組み合わせることで、非同期プログラミングを最大限に活用することができます。
イワケンイワケン

Recommendations

Do's

  • Do have event-based timings loop independently
  • Do use Await() with concurrency expressions -ex: sync, race
  • Do use event.Await() for events so they can be auto cleaned up etc.

Don'ts

  • Don't group event-based timings together in a single loop
  • Don't use Await() calles one after another - the'yll run serially
  • Don't use Subscrive() if possible
  • Don't use rush/branch blind

日本語訳

このスライドでは、非同期プログラミングにおける良い実践方法(Do's)と避けるべき事項(Don'ts)について説明しています。

Do's

  • "イベントベースのタイミングループは独立して持つこと":各イベントはそれぞれ独立したタイミングループで管理すると、それぞれのイベントが互いに影響を与えることなく、自由に動作することができます。
  • "Await()を同時実行表現(例:sync、race)と共に使用すること":Await()は非同期タスクが完了するのを待つための関数です。これを同時実行表現と組み合わせることで、複数の非同期タスクを効率的に管理することができます。
  • "イベントのためにevent.Await()を使用すること":これにより、イベントが自動的にクリーンアップされるなど、イベントのライフサイクルをより効果的に管理することができます。

Don'ts

  • "イベントベースのタイミングを単一のループで一緒に管理しないこと":これは上述の"Do's"と関連しており、各イベントはそれぞれ独立したタイミングループで管理すべきです。
  • "Await()を一つずつ呼び出さないこと":Await()関数を連続して呼び出すと、各タスクが直列に実行され、並行性が失われます。
  • "可能な限りSubscribe()を使用しないこと":Subscribe()はイベントの購読を行いますが、これを使用するとイベントのライフサイクルの管理が難しくなる場合があります。
  • "rush/branchを盲目的に使用しないこと":これらの関数は非同期タスクの生成と管理を行いますが、適切な理解と管理なしに使用すると、予期しない問題を引き起こす可能性があります。
    このスライドは、非同期プログラミングにおける一般的なベストプラクティスと、一般的な間違いを避けるためのアドバイスを提供しています。