[英]C#: BackgroundWorker cloning resources?
我两天来一直在努力解决这个问题,而且只是用尽了想法。 一点......背景:我们有一个WinForms应用程序需要访问数据库,从该数据构造相关的内存中对象列表,然后显示在DataGridView上。 重要的一点是,我们首先填充应用程序范围的缓存(List),然后为DGV所在的表单创建本地缓存的镜像(使用List构造函数参数)。
因为获取数据需要花费几秒钟(DB位于LAN服务器上)来加载,所以我们决定使用BackgroundWorker,并且只在加载数据后刷新DGV。 但是,似乎通过BGW进行加载会导致一些内存泄漏...或者我的错误。 当使用阻塞方法调用加载时,该应用程序消耗大约30MB的RAM; 使用BGW,这可以达到80MB! 虽然它可能看起来不那么多,但我们的客户对它并不太满意。
形成
private void MyForm_Load(object sender, EventArgs e)
{
MyRepository.Instance.FinishedEvent += RefreshCache;
}
private void RefreshCache(object sender, EventArgs e)
{
dgvProducts.DataSource = new List<MyDataObj>(MyRepository.Products);
}
知识库
private static List<MyDataObj> Products { get; set; }
public event EventHandler ProductsLoaded;
public void GetProductsSync()
{
List<MyDataObj> p;
using (MyL2SDb db = new MyL2SDb(MyConfig.ConnectionString))
{
p = db.PRODUCTS
.Select(p => new MyDataObj {Id = p.ID, Description = p.DESCR})
.ToList();
}
Products = p;
// tell the form to refresh UI
if (ProductsLoaded != null)
ProductsLoaded(this, null);
}
public void GetProductsAsync()
{
using (BackgroundWorker myWorker = new BackgroundWorker())
{
myWorker.DoWork += delegate
{
List<MyDataObj> p;
using (MyL2SDb db = new MyL2SDb(MyConfig.ConnectionString))
{
p = db.PRODUCTS
.Select(p => new MyDataObj {Id = p.ID, Description = p.DESCR})
.ToList();
}
Products = p;
};
// tell the form to refresh UI when finished
myWorker.RunWorkerCompleted += GetProductsCompleted;
myWorker.RunWorkerAsync();
}
}
private void GetProductsCompleted(object sender, RunWorkerCompletedEventArgs e)
{
if (ProductsLoaded != null)
ProductsLoaded(this, null);
}
在主线程上调用GetProductsSync或GetProductsAsync,如上所示。 是不是GarbageCollector会因两个线程而丢失? 或者是任务管理器显示不正确的值?
对任何回应,建议和批评都会很有信心。
有趣的是 - 遵循Henk的建议并使用了真正的分析器(.Net Memory Profiler)而不是任务管理器。
虽然mem使用数字几乎相同,但是在同步和异步情况下,MyDataObj实例的预期数量等于预期的(db),虚拟内存和堆大小也非常接近......仍然有些奇怪的事情正在发生。 由于ntdll对VirtualAlloc()的调用,总有1.5MB的差异。 其中大约1MB来自DllUnregisterServerInternal(),在异步情况下占用18.7MB(相对于17.7MB)。 其余大多数来自CoUninitializeEE(),它在异步版本中调用,但不会被同步应用程序调用(?)。 我知道,这是深陷泥潭 - 道歉。 以上1.5MB是我能找到的唯一真正的区别 - 只是我猜测它可能是其他事情发生的迹象。
真正的问题是 :为什么任务经理显示出截然不同的数字? 它不能很好地处理BackgroundWorkers吗? 您是否遇到过如此巨大的差异(30MB对80MB )?
我不完全确定这是否会有所帮助,但在Async方法中你可以改变这个:
List<MyDataObj> p;
using (MyL2SDb db = new MyL2SDb(MyConfig.ConnectionString))
{
p = db.PRODUCTS
.Select(p => new MyDataObj {Id = p.ID, Description = p.DESCR})
.ToList();
}
Products = p;
对此:
using (MyL2SDb db = new MyL2SDb(MyConfig.ConnectionString))
{
Products = db.PRODUCTS
.Select(p => new MyDataObj {Id = p.ID, Description = p.DESCR})
.ToList();
}
我不认为你需要额外的列表变量。 那可能是为什么? 你正在创建一个额外的列表? 无论哪种方式,这也是一个更清洁的看:)
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.