Initialize Async Only Once Pattern

Let's say that I have a class with members that require asynchronous actions to initialize (like I / O files or web requests). I only need to initialize once and I don't want to reinitialize.

Are Tasks and Async-Await good for this?

Here's an example of what I am currently doing:

private Task _initializeTask;
public Task InitializeAsync()
{
    return _initializeTask ?? (_initializeTask = Task.Run(
            async () =>
            {
                // Do an action requiring await here
                await _storageField.LoadAsync();
            }));
}

      

Does it do what I think it does? Are there any better ways to do this?

Is it thread safety? Not required but should be considered.

Changes:

What do I think he is doing? I believe that if _initializeTask has not been assigned, a new task will be assigned to it, which will start and then wait for the asynchronous lambda contained within. Any subsequent calls to the method wait for an already started (or completed) task that has been assigned to _initializedTask.

When do I want to build it? I usually use this method for a service that I resolve with an IoC container. Several dependent classes can be built with a class reference. Then, each of them waits for InitializeAsync () before being used. If theres multiple dependent classes, then I don't want to double it on initialization.

Factory Method? There are usually no multiple instances created that need to be initialized, so the Factory method doesn't seem like a good solution. I used something like the static CreateAsync () method for things like folder wrapper classes, but that didn't allow me to inject initialized folders into the constructors. Asynchronous Factory methods don't get anything unless they can be used with IoC constructor injection.

+3


source to share


2 answers


Your code will work, but it is not thread safe, _initializeTask

you can change after checking it for null

and before initializing it. This will result in two initializations. I would consider using AsyncLazy<T>

which inherits from Lazy<T>

, which is thread safe.

Then, if it LoadAsync

returns Task

, and not Task<T>

, your code becomes (untested):

private AsyncLazy<object> initializeTask = new AsyncLazy<object>(async () =>
            {
                // Do an action requiring await here
                await _storageField.LoadAsync();
                return null;
            });

public Task InitializeAsync()
{
    return _initializeTask.Value;
}

      

You can also define a non-generic version of "AsyncLazy" so you don't need to return a value from the initialization routine.



public class AsyncLazy : Lazy<Task> 
{ 
    public AsyncLazy(Func<Task> taskFactory) : 
        base(() => Task.Run(taskFactory)) { } 
}

      

Then you can initialize it with an initialization method, however this method must be a static compiler:

private AsyncLazy _initializeTask = new AsyncLazy(LoadStorageAsync);

private static async Task LoadStorageAsync()
{   
    // Do an action requiring await here
    await _storageField.LoadAsync();
}   

public Task InitializeAsync()
{
    return _initializeTask.Value;
}

      

+5


source


Start an asynchronous task from a regular initialization function. In a normal function, check if your application has a dataset already loaded that meets your expectations. If there is no data, then activate the async function.

...
If (BooleanFunctionToDetermineIfDataIsNotPresent){FunctionToLoadFreshData}
...


Private Async Void FunctionToLoadFreshData{...}

      



The data load function shouldn't return a value so that it doesn't become the task itself.

0


source







All Articles