C #: BackgroundWorker cloning resources?

Problem

For two days now I have been struggling with this problem from the side, and my ideas are running out. A little ... background: we have a WinForms application that needs to access the database, build a list of related objects in memory from this data, and then display it in a DataGridView. The important point is that we first populate the cache application (List), and then create a mirror of the local cache in the form on which the DGV lives (using the list constructor parameter).

Since it takes several seconds to load the data (the database is located on the local network server) to load, we decided to use BackgroundWorker and update the DGV only after the data was downloaded. However, it seems that when loading via BGW there is a memory leak ... or an error on my part. When downloading using the lock method, the application consumes about 30 MB of RAM; with BGW it's a jump of up to 80 MB! Although this may not seem like that in any case, our customers are not too happy with it.

Corresponding code

The form

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

Repository

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

To the end!

GetProductsSync or GetProductsAsync is called in the main thread, not shown above. Maybe GarbageCollector is just lost with two threads? Or does the task manager show incorrect values?

Will be welcome for any answers, suggestions, criticism.

+4
source share
2 answers

It's funny that he followed Henk’s advice and used a real profiler (.Net Memory Profiler), not a task manager.

While the memory usage numbers are almost identical, the expected number of MyDataObj instances equal to the expected (db) in both synchronization and asynchronous cases, the sizes of the virtual memory and heap are also very close ... something interesting is still happening. There is always a 1.5 MB difference that is associated with calling VirtualAlloc () using ntdll. About 1MB of this comes from DllUnregisterServerInternal (), which takes up 18.7 MB in the asynchronous case (versus 17.7 MB). Most of the rest comes from CoUninitializeEE (), which is called in the asynchronous version, but not called by the synchronization application (?). I know this is delving into the mud - an apology. The above 1.5MB is the only real difference that I could find - just my wild assumption that this might be a sign of something else happening.

The real question is : why does the task manager show completely different numbers? Does it work well with BackgroundWorkers? Have you ever encountered such a big difference (30 MB vs 80 MB)?

+1
source

I'm not quite sure if this will help, but in Async method you can change this:

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

I do not think you need an extra list variable. Maybe why? Have you created a whole additional list? Anyway, it also looks a little cleaner :)

0
source

Source: https://habr.com/ru/post/1304768/


All Articles