Tuesday, June 20, 2017

How to initialize instances asyncronously

Here is another problem I've been thinking about quite a while, before found a solution I like.
async/await keywords revolutionized the writing of asynchronous code in C#. It is as simple as marking a method with async keyword and using await in it to wait for asynchronous operations to complete in non-blocking manner.This gets trickier with async initialization methods. Think about a class, instances of which need to be initialized once only, before those can be used. In a non-async scenario one example would be to call the initialization method from the constructor, as follows:

public class SomeDirectory
{
  private Object state;

  public SomeDirectory()
  {
    this.Initialize();
  }

  // This method expects the state to be initialized before execution
  public void DoSomething()
  {
    // some logic
  }

  private void Initialize()
  {
    this.state = // state retrieval logic
  }
}

This gets complicated, when the initialization method becomes asynchronous. As constructors are non-async methods, the following transformation can lead to a situation, where instances of the SomeDirectory class aren't yet initialized after constructor, which can lead to some unexpected side-effects later, when the DoSomething method is called. The below sample code emphasizes the situation:


public class SomeDirectory
{
  private Object state;

  public SomeDirectory()
  {
    this.InitializeAsync();
  }

  // This method expects the state to be initialized before execution
  public async Task DoSomethingAsync()
  {
   // some logic
  }

  private async Task InitializeAsync()
  {
    this.state = // state retrieval logic
  }
}

A simple solution to this would be to force the constructor to wait for the initialization to complete:


  public SomeDirectory()
  {
    this.InitializeAsync().GetAwaiter().GetResult();
  }


This, however, is not a good technique, as it will introduce delay in the caller thread, which can harm in many situations (like in server code, where the actual DoSomethingAsync api is not being called immediately).

The third approach is calling initialization on every public API. Of course with a simple "run-only-once" optimization in it. Something like this:


public class SomeDirectory
{
  private readonly Object stateLock = new Object();
  private Object state;

  public SomeDirectory()
  {
  }

  // This method expects the state to be initialized before execution
  public async Task DoSomethingAsync()
  {
    await this.InitializeAsync();

   // some logic
  }
}

The problem with this approach is that it will slow down the very first call to DoSomethingAsync() method. Sometimes - it's something you don't want to have.
So here comes the final approach, which I'm using nowdays:


public class SomeDirectory
{
  private Object state;
  private readonly Task initializationTask;

  public SomeDirectory()
  {
    this.initializationTask = this.InitializeAsync();
  }

  // This method expects the state to be initialized before execution
  public async Task DoSomethingAsync()
  {
    await this.initializationTask;

   // some logic
  }

  private async Task InitializeAsync()
  {
    this.state = // state retrieval logic
  }
}

This approach will allow the execution of the initialization start as soon as the object is constructed. Yet, it won't loose the track of its execution.

Hope this will help some of you.

No comments: