14

如果我想对某些代码进行“一劳永逸”,但仍想确保清理我的内存(根据Why does asynchronous delegate method requires calling EndInvoke?),以下是否可以实现该目标?

Action myAction = () => LongRunTime();
myAction.BeginInvoke(myAction.EndInvoke,null);

我环顾四周,但没有看到在任何地方使用过这种模式。相反,人们使用 annonomoyus 方法作为他们的回调(例如The proper way to end a BeginInvoke?)或者他们定义一个实际的回调方法。由于我没有看到其他人这样做,这让我认为它要么不起作用,要么是一个坏主意。

谢谢!

4

2 回答 2

15

使用方法组转换而不是委托很好,EndInvoke仍然会在您的Action. 没有什么可做的,因为这是一个“一劳永逸”的电话。

不幸的是,直接无可辩驳地证明调用了 EndInvoke 有点困难,因为Action它是一个委托,我们不能只在 BCL 中的某个类上添加断点。

此代码将(定期)检查由 返回的 IAsyncResult 的一些私有字段BeginInvoke,这似乎跟踪是否EndInvoke已被调用:

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;
            }
        }
    }
}

我已经使用SOS仔细检查了没有任何托管内存泄漏。我还尝试了其他几个证明,但我认为它们比这个更间接。

我在调查期间发现了一些有趣的事情:myAction.BeginInvoke调用将显示在使用仪器的分析器上,但myAction.EndInvoke不会。

于 2013-04-12T22:21:40.250 回答
1

现在它可以像

BeginInvoke((Action)(async () =>
{
    // Show child form
    var f = new MyForm();
    f.ShowDialog();
    // Update parent/current
    await UpdateData();
}));
于 2018-03-30T04:47:49.940 回答