using System.Threading.Tasks;
public static async Task Main()
var stopSource = new CancellationTokenSource();
IClient c = new Client();
ITokenProvider tp = new TokenProvider(c, stopSource.Token);
Console.WriteLine(await tp.GetTokenAsync());
Console.WriteLine(await tp.GetTokenAsync());
Console.WriteLine(await tp.GetTokenAsync());
Console.WriteLine(await tp.GetTokenAsync());
var test1 = tp.GetTokenAsync();
var test2 = tp.GetTokenAsync();
Console.WriteLine(object.ReferenceEquals(test1,test2));
public interface ITokenProvider
Task<string> GetTokenAsync();
Task<string> RefreshAsync();
public class TokenProvider : ITokenProvider
private const double REFFRESH_IN_FRACTIONS_OF_SECOND = 0.5;
private readonly IClient _client;
private readonly Task _expirationTask;
public TokenProvider(IClient client, CancellationToken stoppingToken)
_expirationTask = Task.Run( async () =>
var pt = new PeriodicTimer(TimeSpan.FromSeconds(REFFRESH_IN_FRACTIONS_OF_SECOND));
while( !stoppingToken.IsCancellationRequested )
await pt.WaitForNextTickAsync(stoppingToken);
if( !stoppingToken.IsCancellationRequested ) Expire();
private Task<string> RefreshTokenAsync()
=> _client.RefreshAsync();
private Lazy<Task<string>>? _lazyToken = null;
private Lazy<Task<string>> LazyToken()
_ = Interlocked.CompareExchange(ref _lazyToken, new Lazy<Task<string>>( async () => {
return await _client.RefreshAsync();
private void Expire() => Interlocked.Exchange(ref _lazyToken, null);
public Task<string> GetTokenAsync() => LazyToken().Value;
public sealed class Client : IClient
public async Task<string> RefreshAsync()
return Random.Shared.Next().ToString();