Actions/AsyncCallbacks and Memory Allocation
-
28-06-2021 - |
Question
I am a bit of a n00b with memory allocation, but I am trying to understand how using AsyncCallbacks with the BeginInvoke type design pattern might cause a memory allocation and thusly might cause the garbage collector to be invoked more frequently.
For instance, if I had a class with an event that I wished to invoke asynchronously with a callback, I would normally set it up as follows:
Method I
public class PriceBook
{
private double Value;
public void UpdateValue(double Value)
{
this.Value = Value;
}
public event EventHandler ValueUpdated;
private void DoValueUpdated(double Value)
{
if (ValueUpdated != null)
{
ValueUpdated.BeginInvoke(this, EventArgs.Empty, ValueUpdateCompleted, Value);
}
}
private void ValueUpdateCompleted(IAsyncResult ar)
{
// Do something when event has finished being called.
}
}
Somebody that I work with says that calling the BeginInvoke in this way actually allocates memory by creating a new AsyncCallback every time it is called. So, he suggested doing it like this instead:
Method II
public class PriceBook
{
private AsyncCallback ValueUpdateCallback;
private double Value;
public PriceBook1()
{
ValueUpdateCallback = ValueUpdateCompleted;
}
public void UpdateValue(double Value)
{
this.Value = Value;
DoValueUpdated(Value);
}
public event EventHandler ValueUpdated;
private void DoValueUpdated(double Value)
{
if (ValueUpdated != null)
{
ValueUpdated.BeginInvoke(this, EventArgs.Empty, ValueUpdateCallback, Value);
}
}
private void ValueUpdateCompleted(IAsyncResult ar)
{
// Do something when event has finished being called.
}
}
As you can see, here I set the AsyncCallback once in the constructor. Does method II really save on memory allocation? Are there any other benefits of either method/is there an even better way of accomplishing what I am trying to do?
- William
Solution
Yes, that saves one delegate allocation.
This
ValueUpdated.BeginInvoke(..., ValueUpdateCompleted, ...);
is really this:
ValueUpdated.BeginInvoke(..., new AsyncCallback(ValueUpdateCompleted), ...);
You can see the allocation now.
If the invocation is not on a hot path, meaning that it is not performance critical, I recommend not to cache the delegate because code maintainability is suffering from it.
Also, a thread-pool invocation is a lot more expensive than that delegate allocation. I cannot imagine a situation where this might make a difference.