You can easily implement the required logic using tasks and async
/await
.
The doRefreshData
method should return a Task<TResult>
of the appropriate type. Here's a dummy method that does nothing for two seconds and then returns a "result":
async Task<object> RealGetData()
{
await Task.Delay(2000);
return 42;
}
Since you want multiple methods to request the data concurrently and have their requests satisfied with the same result, you need to somehow remember that there is a "get data" operation already underway. You also need some kind of thread synchronization to eliminate race conditions, so:
private Task<object> currentCalculation;
private object lockTarget = new object(); // just for lock()
Now it's trivial to write an async
method that either starts a new calculation if none is pending, or else hooks you up to receive the result of the pending one:
async Task<object> GetData()
{
lock (lockTarget)
{
if (currentCalculation != null && !currentCalculation.IsCompleted)
{
return currentCalculation;
}
return currentCalculation = Task.Run<object>(RealGetData);
}
}
Using this is super easy: any time you want access to "fresh" data, write await GetData()
and you will always get a new result back; the code will automatically queue you up to get the result being currently calculated or start a new calculation for you. For example, this will start off a single calculation and satisfy all 10 requests with its result:
for (var i = 0; i < 10; ++i) {
Console.WriteLine(await GetData());
}