If I want to do a \"fire and forget\" of some code, but still want to ensure that my memory is cleaned up (per Why does asynchronous delegate method require calling EndInvoke?),
Using a method group conversion instead of a delegate is fine, the EndInvoke
will still be called in on your Action
. There is nothing else to be done, since this is a fire and forget call.
Unfortunately, it's somewhat hard to directly irrefutably prove that EndInvoke is called, since Action
is a delegate and we can't just add a breakpoint on some class in the BCL.
This code will (periodically) inspect some private field of the IAsyncResult that is returned by BeginInvoke
, which seems to keep track of whether or not EndInvoke
has been called yet:
public partial class MainWindow : Window
{
private Timer _timer = new Timer(TimerCallback, null, 100, 100);
private static IAsyncResult _asyncResult;
public MainWindow()
{
InitializeComponent();
}
static void LongRunTime()
{
Thread.Sleep(1000);
}
void Window_Loaded(object sender, RoutedEventArgs args)
{
Action myAction = () => LongRunTime();
_asyncResult = myAction.BeginInvoke(myAction.EndInvoke, null);
}
static void TimerCallback(object obj)
{
if (_asyncResult != null)
{
bool called = ((dynamic)_asyncResult).EndInvokeCalled;
if (called)
{
// Will hit this breakpoint after LongRuntime has completed
Debugger.Break();
_asyncResult = null;
}
}
}
}
I've double checked using SOS that there aren't any managed memory leaks. I've also tried several other proofs, but they were more circumstantial than this one, I think.
Some interesting I discovered during my investigation: the myAction.BeginInvoke
call will show up on profilers using instrumentation, but myAction.EndInvoke
does not.
Nowdays it could be done like
BeginInvoke((Action)(async () =>
{
// Show child form
var f = new MyForm();
f.ShowDialog();
// Update parent/current
await UpdateData();
}));