繁体   English   中英

ViewModel中的Task导致的内存泄漏

[英]Memory Leak caused by Task in ViewModel

我有以下代码,它导致内存泄漏。

问题是任务,当我删除它时,一切都很好,并且View和ViewModel都已GC。 似乎Task保留了对UpdateTimeDate的引用,因此也保留了对ViewModel的引用。 我尝试了各种方法,但都没有成功,希望有人对此有任何想法或解释。

public class HeaderViewModel : Observable, IDisposableAsync
{
    public HeaderViewModel (IDateTimeProvider dateTimeProvider)
    {
        TokenSource = new CancellationTokenSource();

        ATask = Task.Run(
            async () =>
            {
                while(!TokenSource.Token.IsCancellationRequested)
                {
                    UpdateTimeData();
                    await Task.Delay(800);
                }

                IsDisposed = true;
            },
            TokenSource.Token);

        UpdateTimeData();

        void UpdateTimeData()
        {
            TimeText = dateTimeProvider.Now.ToString("HH:mm:ss");
            DateText = dateTimeProvider.Now.ToString("dd.MM.yyyy");
        }
    }

    public CancellationTokenSource TokenSource { get; set; }

    public bool IsDisposed { get; set; }

    public string TimeText
    {
        get => Get<string>();
        set => Set(value);
    }

    public string DateText
    {
        get => Get<string>();
        set => Set(value);
    }

    private Task ATask { get; set; }

    public async Task Dispose()
    {
        TokenSource.Cancel();

        while(!IsDisposed)
        {
            await Task.Delay(50);
        }

        TokenSource.Dispose();
        ATask.Dispose();
        ATask = null;
        TokenSource = null;
    }
}

这是基于计时器的解决方案,它还会导致内存泄漏:

public class HeaderViewModel : Observable, IDisposableAsync
{
    public HeaderViewModel(IDateTimeProvider dateTimeProvider)
    {
        DateTimeProvider = dateTimeProvider;

        ATimer = new Timer(800)
        {
            Enabled = true
        };

        UpdateTimeData(this, null);

        ATimer.Elapsed += UpdateTimeData;
    }

    public string TimeText
    {
        get => Get<string>();
        set => Set(value);
    }

    public string DateText
    {
        get => Get<string>();
        set => Set(value);
    }

    public bool IsDisposed { get; set; }

    private IDateTimeProvider DateTimeProvider { get; }

    private Timer ATimer { get; }

    public async Task Dispose()
    {
        ATimer.Stop();

        await Task.Delay(1000);

        ATimer.Elapsed -= UpdateTimeData;
        ATimer.Dispose();
        IsDisposed = true;
    }

    private void UpdateTimeData(object sender, ElapsedEventArgs elapsedEventArgs)
    {
        TimeText = DateTimeProvider.Now.ToString("HH:mm:ss");
        DateText = DateTimeProvider.Now.ToString("dd.MM.yyyy");
    }
}

我找到了解决方案。 感谢keuleJ,他发表了将我引向它的评论。 因此,当您创建任务或计时器时,它们都将捕获ViewModel的实例。 防止它的方法是制作WeakReference并使用它:

public class HeaderViewModel : Observable, IDisposableAsync
{
    public HeaderViewModel(IDateTimeProvider dateTimeProvider)
    {
        DateTimeProvider = dateTimeProvider;

        UpdateTimeData();

        var weakReference = new WeakReference(this);

        Task.Run(
            async () =>
            {
                while(!((HeaderViewModel)weakReference.Target).IsDisposing)
                {
                    ((HeaderViewModel)weakReference.Target).UpdateTimeData();
                    await Task.Delay(800);
                }

                ((HeaderViewModel)weakReference.Target).IsDisposed = true;
            });
    }

    public bool IsDisposed { get; set; }

    public string TimeText
    {
        get => Get<string>();
        set => Set(value);
    }

    public string DateText
    {
        get => Get<string>();
        set => Set(value);
    }

    private IDateTimeProvider DateTimeProvider { get; }

    private bool IsDisposing { get; set; }

    public async Task Dispose()
    {
        IsDisposing = true;

        while(!IsDisposed)
        {
            await Task.Delay(50);
        }
    }

    private void UpdateTimeData()
    {
        TimeText = DateTimeProvider.Now.ToString("HH:mm:ss");
        DateText = DateTimeProvider.Now.ToString("dd.MM.yyyy");
    }
}

请注意,我也无法使用

(HeaderViewModel)weakReference.Target

一旦我这样做,似乎就发生了一些魔术,实例将再次被捕获。

您的Dispose任务似乎永不返回,这就是您的对象保留在内存中的原因。 我找到了问题所在

await Task.Delay(1000)

如果您根据此帖子https://stackoverflow.com/a/24539937/3084003进行更改,它将可以正常工作

await Task.Delay(1000).ConfigureAwait(false);

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM