c# - ViewModel中的Task导致的内存泄漏
问题描述
我有以下代码,它会导致内存泄漏。
问题是任务,当我删除它时,一切都很好,并且 View 以及 ViewModel 都被 GCed。似乎任务保留了对 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;
}
}
这是基于 Timer 的解决方案,它也会导致内存泄漏:
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,他发布了引导我的评论。因此,当您创建其中任何一个时,Task 或 Timer 正在捕获 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
一旦我这样做了,一些魔法似乎发生了,实例将再次被捕获。
推荐阅读
- gitlab - 我可以配置一个依赖于手动作业的 gitlab CI 作业吗?
- r - 使用列表/数据框作为 R 中 for 循环中的项目
- webdriver-io - 如何从 webdriverIO 中的工具提示中定位和获取文本
- postgresql - 普通人表与教师和学生的特定表或单独的表?
- date - Spotfire - 如何根据日期和时间构建序列号
- php - 第 39 行似乎有一个错误,我无法弄清楚它是什么,请指出我的错误?
- python - 如何避免使用 SymPy 减少复杂分数
- css - 溢出和 Z 指数问题
- node.js - 从服务器的标头中获取标头令牌
- php - 从mysql获取图像的未定义索引