[英]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.