Can I make protected override void Update async?

I’m learning asynchronous programming and I want to know if I can change protected override void Update to protected override async void Update so that I can use await in this method. Is it feasible to make the Update method async or will this just cause problems(exceptions, game crashes, …)?

Does the Draw method behave differently if I use await in Update method? Will the Draw method be executed normally or will it not be executed regularly if I use await in Update method?

I want to develop a game for iOS and Android. Is it possible to use my code on those platforms or is it not a good idea to make the Update method async?

  Microsoft.Xna.Framework.Rectangle CursorRectangle, LoginButtonRectangle, LevelmenuButtonRectangle;
  bool IsButtonPressed = false;
  float ElapsedTimeSinceLastFrame = 0f;
  bool AllTasksCompleted = false;

  public async Task PlayerTasks()
  {
        var login =  PlayerLogin();
        var inventory = GetPlayerInventory();
        var statistics = GetPlayerStatistics();
        var TasksList = new List<Task> { login, inventory, statistics };
        await Task.WhenAll(TasksList);
  }

  private async Task PlayerLogin()
  {
      // ...
  }

  private async Task GetPlayerInventory()
  {
      // ...
  }

  private async Task GetPlayerStatistics()
  {
      // ...
  }

  protected override async void Update(GameTime gameTime)
  {
         ElapsedTimeSinceLastFrame = (float)gameTime.ElapsedGameTime.TotalSeconds;

         while (TouchPanel.IsGestureAvailable)
         {
             GestureSample gs = TouchPanel.ReadGesture();
             switch (gs.GestureType)
             {
                 case GestureType.Tap:
                 CursorRectangle = new Microsoft.Xna.Framework.Rectangle((int)gs.Position.X, (int)gs.Position.Y, 10, 10);
                 IsButtonPressed = true;
                 break;
             }
          }

          if ((LoginButtonRectangle.Intersects(CursorRectangle)) && (IsButtonPressed == true))
          {
              await PlayerTasks();
              AllTasksCompleted = true;
          }       

          if ((LevelmenuButtonRectangle.Intersects(CursorRectangle)) && (AllTasksCompleted == true) && (IsButtonPressed == true))
          {
              // ...
          }       

          IsButtonPressed = false;

          base.Update(gameTime);
    }   

    protected override void Draw(GameTime gameTime)
    {
        graphics.GraphicsDevice.Clear(Microsoft.Xna.Framework.Color.CornflowerBlue);
        spriteBatch.Begin();
        // ...
        spriteBatch.End();
    
        base.Draw(gameTime);
    }

you can override with async to use await - Update is working as usual.

Your PlayerTasks is basically converted to a State Machine by using the async things, so that should basically work on every platform as it’s nothing specific, just compiler magic. No threads involved.

Be aware, that all your code is still running in the same thread and async may not bring the benefit you expect to have (until you have some sleeping in there or several I/O which happens to be asyncronous by nature and relying on hardware interrupts after completion)

You also may be aware of changing base data like your inventories etc which can bring you in lots of trouble which is hard to debug. As long as you work with full copies in your PlayerTasks Function everything should be ok in your case I guess

While async is pretty practical to code, it may not always bring the expected behaviour and personally, I prefer doing everything manually in such situations just to make it easier to debug or control in the games flow.

In my voxel engine I actually use the hardest way and handle several threads with all sorts of data syncronisation tricks (not using lock that often) to not block the update thread with long running (CPU) operations, which brings tons of headache.

So - as a sugestion - if you can get away with linear code flow: do it. Maybe split your code to several states manually, like handle only one “x” tasks per frame and do the others in the next frame. You will have a much easier time debugging and don’t really need to care for data syncronization

Hi Hobbit7,

From experience, I would avoid this like the plaque.

You are setting yourself up for all sorts of nasty problems that are really easy to fix, but just about impossible to find… A task that finishes after another in debug , and before it in release. A variable that gets initialised in debug , but not in release.

I can’t talk about other platforms, but on windows the update and draw methods are run on different cores of the CPU which is really nice.

From your small code sample above, I would suggest you think about changing your design.

One trick I use sometimes is a Monogame version of fibers. I basically create a single thread on a free core and on this I run a simple code design with the only async code when the logic requires it.

It’s hard to explain, but very simple to code.

The best example I can give is old c code that used getch() to control flow.

So display a menu, call getch to get key input . Switch on the input and call other functions.

In this model everything is the same except when the old code calls getch, the new code sleeps and scans for input N milliseconds later.

I hope you can see what I mean, it’s simple but very powerful.

Would it be better to run PlayerTasks() in a different thread? Is it possible to create a new thread for PlayerTasks() without making Update async?

“better” highly depends on your game :slight_smile: If your PlayerTasks are highly time intense, then yes otherwise I don’t think the added complexity (which is a lot) will be justified by going multi-threaded.

Your approach may be alright, I just said, that I would not use asnyc behaviour but do the task distribution manually. But I also may just be different and just enjoy the direct control:-) There’s technically nothing wrong with your approach

And there is a third option, let C# do it for you

https://docs.microsoft.com/en-us/dotnet/standard/parallel-programming/how-to-write-a-simple-parallel-foreach-loop

C# has built in multithreading if your code structure can use it.

1 Like

thanks, I completely forget about that - my recent work on games included so much optimization tinkering (not using iterators, sparing function calls etc) that I already lose those practical things which c# provides :smiley:

“better” highly depends on your game :slight_smile: If your PlayerTasks are highly time intense, then yes otherwise I don’t think the added complexity (which is a lot) will be justified by going multi-threaded.

Ok, then I don’t need to use a new thread, because PlayerTasks are not highly time intense. In PlayerTasks, I make API calls to log in the player on the server, get the player’s save game data(json data) and receive new notifications. I think that will just take a few seconds.

Well in that case, async is a good solution, as you have lots of I/O there :slight_smile:

1 Like

Do not make Update async.

await means return here and continue the rest of this method when this operation finishes. You do not want to wait in your update method for a network call or heavy file IO to complete. MonoGame does not know your Update method is async and will not wait for it to fully complete, instead continuing the game loop when you first call await. That means that if your Update method takes longer than your target frame rate MonoGame will call it again before it finishes. If you properly separate draw and update logic your frame rate won’t suffer because MG calls draw as usual, but you might run into subtler issues like race conditions.

Instead do not await the call to your PlayerTasks method and store the resulting Task in a field. Then check for completion inside Update. Make sure you check if the task finished with errors.

1 Like

Is it possible to make the Load method async? Will Update and Draw only be executed after PlayerTasks() has been fully completed?

public async void Load(ContentManager content) 
{ 
    await PlayerTasks();
    // if PlayerTasks() is successful, then continue with the next line:
    MySprite = content.Load<Texture2D>("AssetName");
}

What if you try this? https://docs.microsoft.com/en-us/dotnet/api/system.threading.tasks.task.wait?view=netframework-4.8

public void Load(ContentManager content) 
{ 
    PlayerTasks().Wait();
    // if PlayerTasks() is successful, then continue with the next line:
    MySprite = content.Load<Texture2D>("AssetName");
}

I have tried it with .Wait() but it doesn’t work. The following code always returns false.

    protected override void LoadContent()
    {
        bool Testwait = RegisterGuestPlayFabAccountTest().Wait(10000);
    }

    private async Task RegisterGuestPlayFabAccountTest()
    {
        PlayerDeviceId = ReturnMobileID();
        var result = await PlayFabClientAPI.LoginWithIOSDeviceIDAsync(new LoginWithIOSDeviceIDRequest()
        {
            DeviceId = PlayerDeviceId,
            CreateAccount = true
        });

        if (result.Error != null)
        {

        }
        else
        {

        }
}

The line “var result = …” is executed but next line “if (result.Error != null)” is never executed. Testwait is always false.

But it works when I make the Load method async. Then, all my code is executed normally.

    protected async override void LoadContent()
    {
        await RegisterGuestPlayFabAccountTest();
    }

Can I make the Load method async or will this maybe cause problems with the rest of the code?

Just out of curiosity, what happens if you do this? https://docs.microsoft.com/en-us/dotnet/api/system.threading.tasks.task-1.result?view=netframework-4.8

    protected override void LoadContent()
    {
        bool Testwait = RegisterGuestPlayFabAccountTest().Result;
    }

    private async Task<bool> RegisterGuestPlayFabAccountTest()
    {
        PlayerDeviceId = ReturnMobileID();
        var result = await PlayFabClientAPI.LoginWithIOSDeviceIDAsync(new LoginWithIOSDeviceIDRequest()
        {
            DeviceId = PlayerDeviceId,
            CreateAccount = true
        });

        return result.Error != null;
}

Nothing happens after var result = await PlayFabClientAPI.LoginWithIOSDeviceIDAsync is executed and I get no error message. The next line return result.Error != null; is never executed.