C #: ресурсы клонирования BackgroundWorker? - PullRequest
4 голосов
/ 22 марта 2010

Проблема

Я боролся с этой частичной проблемой уже два дня и просто иссякли идеи.Немного ... предыстория: у нас есть приложение WinForms, которое должно получить доступ к базе данных, создать список связанных объектов в памяти из этих данных и затем отобразить в DataGridView.Важным моментом является то, что мы сначала заполняем кэш приложения (List), а затем создаем зеркало кеша, локальное для формы, в которой живет DGV (используя параметр конструктора List).

Поскольку происходит выборка данныхзагрузка занимает несколько секунд (БД находится на сервере ЛВС), мы решили использовать BackgroundWorker и обновлять DGV только после загрузки данных.Однако, похоже, что загрузка через BGW приводит к некоторой утечке памяти ... или ошибке с моей стороны.При загрузке с использованием вызова метода блокировки приложение потребляет около 30 МБ ОЗУ;с BGW это прыгает до 80 МБ!Хотя в любом случае это может показаться не таким уж большим, наши клиенты не слишком рады этому.

Соответствующий код

Форма

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 просто теряется с двумя потоками?Или это диспетчер задач, который показывает неправильные значения?

Буду благодарен за любые ответы, предложения, критику.

Ответы [ 2 ]

1 голос
/ 22 марта 2010

Забавно, что - следовал совету Хенка и использовал настоящий профилировщик (.Net Memory Profiler), а не диспетчер задач.

Хотя числа записей mem практически идентичны, ожидаемое количество экземпляров MyDataObj, равное ожидаемому (дБ) в случаях как синхронизации, так и асинхронности, размеров виртуальной памяти и кучи также очень близко ... все же что-то любопытно происходит. Разница в 1,5 МБ всегда связана с вызовом VirtualAlloc () через ntdll. Около 1 МБ из этого поступает от DllUnregisterServerInternal (), которая в асинхронном случае занимает 18,7 МБ (против 17,7 МБ). Большая часть остальных происходит от CoUninitializeEE (), который вызывается в асинхронной версии, но не вызывается приложением синхронизации (?). Я знаю, это копание глубоко в грязи - извинения. Приведенные выше 1,5 МБ - единственное реальное различие, которое я смог найти - просто мое дикое предположение, что это может быть признаком чего-то еще.

Реальный вопрос - : почему диспетчер задач показывает совершенно разные цифры? Разве это не хорошо справляется с BackgroundWorkers? Вы когда-нибудь сталкивались с такой огромной разницей (30 МБ 80 МБ )?

0 голосов
/ 22 марта 2010

Я не совсем уверен, поможет ли это, но в методе 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();
}

Не думаю, что вам нужна дополнительная переменная списка. Это может быть почему? Вы создавали целый дополнительный список? В любом случае, это выглядит немного чище:)

...