I am new to C#'s ConcurrentDictionary
class, and I wonder how I can use a valueFactory in the GetOrAdd
method in an async way.
public class Class1
{
public int X = 10;
public Class1(int x)
{
X = x;
Debug.WriteLine("Class1 Created");
}
}
Testing code logic in a WinForm button:
private async void button1_Click(object sender, EventArgs e)
{
var asyncDict = new ConcurrentDictionary<int, Task<Class1>>();
Func<int, Task<Class1>> valueFactoryAsync = async (k) => new Class1(await Task.Run(async () =>
{
await Task.Delay(2000);
Debug.WriteLine("Async Factory Called");
return 5;
}));
var temp = await(asyncDict.GetOrAdd(1, valueFactoryAsync)).ConfigureAwait(false);
Debug.WriteLine(temp.X);
temp.X = 20;
var temp2 = await (asyncDict.GetOrAdd(1, valueFactoryAsync)).ConfigureAwait(false);
Debug.WriteLine(temp2.X);
}
When GetOrAdd method is called in the second time, it returns a Task from the concurrent dictionary. Then I called await on the Task,
var temp2 = await (asyncDict.GetOrAdd(1,valueFactoryAsync)).ConfigureAwait(false);
it seems like the underlying Task(or async method) was not called again and return a new Class1 object.Was it just returned the Class1 object generated in the first call?
Could you advise what is happening under the hood?
Yes, you get the same instance when you call asyncDict.GetOrAdd(1,valueFactoryAsync)
the second time. Why does that surprise you? You have provided the same key as in the first time.
what is happening under the hood?
var temp = await(asyncDict.GetOrAdd(1, valueFactoryAsync)).ConfigureAwait(false);
When this line is called, there is no item in the dictionary for key=1, so valueFactoryAsync
is called, returns a Task with Status=Created, and this Task is added to the dictionary. That's it for the Add
part. Now begins the Get
part, and you get back that new ready-to-run Task. You actually awaits this task (var temp = await (...)
), so the Task begins and your next lines of code won't execute until this Task is done. I hope the rest is quite straightforward from here - the next time you call await (asyncDict.GetOrAdd(1, valueFactoryAsync))
- for temp2
- there is already an item in the dictionary for this key - a completed Task, and that's the Task you get. Note, that even if you weren't await the first call, the dictionary would still had been populated with Created
Task, and on the second call you still get the same Task (and hence the same Class1
instance if you await
/ .Result
it)
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With