.NET Fiddle

CSharp · Console

/Program.cs

using System;
using System.Diagnostics;
using System.Runtime.CompilerServices;
using System.Threading.Tasks;

public class Program
{
    public static async Task Main()
    {
        var test = new Test();

        var task1 = Task.Run(async () => await test.AsyncString());
        var task2 = Task.Run(async () => await test.AsyncString());
        var task3 = Task.Run(async () => await test.AsyncString());

        var results = await Task.WhenAll(task1, task2, task3);

        Console.WriteLine(string.Join(", ", results));
    }
}

public sealed class Test
{
    public Test()
    {
        _lazyString = new AsyncLazy<string>(longRunningOperation);
    }

    public async Task<string> AsyncString()
    {
        Console.WriteLine($"{_sw.Elapsed}: Started awaiting lazy string.");
        var result = await _lazyString;
        Console.WriteLine($"{_sw.Elapsed}: Finished awaiting lazy string.");

        return result;
    }

    async Task<string> longRunningOperation()
    {
        Console.WriteLine($"{_sw.Elapsed}: longRunningOperation() started.");
        await Task.Delay(4000);
        Console.WriteLine($"{_sw.Elapsed}: longRunningOperation() finished.");
        return "finished";
    }

    readonly Stopwatch _sw = Stopwatch.StartNew();

    readonly AsyncLazy<string> _lazyString;
}

public class AsyncLazy<T> : Lazy<Task<T>>
{
    public AsyncLazy(Func<T> valueFactory) :
        base(() => Task.Factory.StartNew(valueFactory))
    { }

    public AsyncLazy(Func<Task<T>> taskFactory) :
        base(() => Task.Factory.StartNew(taskFactory).Unwrap())
    { }

    public TaskAwaiter<T> GetAwaiter() { return Value.GetAwaiter(); }
}
;