私はマルチプレイヤー ゲーム サーバーを作成しており、新しい C# の async/await 機能が役立つ方法を検討しています。サーバーのコアは、ゲーム内のすべてのアクターを可能な限り高速に更新するループです。
while (!shutdown)
{
foreach (var actor in actors)
actor.Update();
// Send and receive pending network messages
// Various other system maintenance
}
このループは、何千ものアクターを処理し、ゲームをスムーズに実行し続けるために毎秒複数回更新するために必要です。一部のアクターは、データベースからデータをフェッチするなど、更新機能で低速のタスクを実行することがあります。これは、非同期を使用したい場合です。このデータが取得されると、アクターはゲームの状態を更新しようとしますが、これはメイン スレッドで行う必要があります。
これはコンソール アプリケーションであるため、保留中のデリゲートをメイン ループにディスパッチできる SynchronizationContext を作成する予定です。これにより、これらのタスクが完了するとゲームが更新され、未処理の例外がメイン ループにスローされるようになります。私の質問は、非同期更新関数をどのように書くのですか? これは非常にうまく機能しますが、async void を使用しないという推奨事項を破っています。
Thing foo;
public override void Update()
{
foo.DoThings();
if (someCondition) {
UpdateAsync();
}
}
async void UpdateAsync()
{
// Get data, but let the server continue in the mean time
var newFoo = await GetFooFromDatabase();
// Now back on the main thread, update game state
this.foo = newFoo;
}
Update() を非同期にして、タスクをメイン ループに戻すこともできますが、次のようになります。
- それを決して使用しない何千もの更新にオーバーヘッドを追加したくありません。
- メインループでも、タスクを待機してループをブロックしたくありません。
- タスクを待機すると、待機中のスレッドで完了する必要があるため、とにかくデッドロックが発生します。
待ちきれないこれらすべてのタスクをどうすればよいですか? それらがすべて完了したことを知りたいのは、サーバーをシャットダウンするときだけですが、おそらく数週間分の更新によって生成されたすべてのタスクを収集したくありません.