2 Проблемы с компонентом BackgroundWorker - PullRequest
1 голос
/ 26 ноября 2009

Во-первых, я знаю, что должен использовать правильные методы Threading (Threadpool, BeginInvoke и т. Д.) Для достижения этой цели, но это немного над моей головой в настоящее время и потребует некоторое время для чтения материала и поймите это (если у вас есть ссылки на URL для моего сценария, пожалуйста, не стесняйтесь их публиковать).

Тем временем я использую backgroundWorker, чтобы получить очень большой набор результатов и заполнить им DatagridView. Я успешно создаю SortableBindingList<TEntities> в своем событии DoWork и передаю его в результате. И в случае RunWorkerCompleted я разыгрываю и связываю это SortableBindingList<TEntities> с моей Решёткой. Мои две основные проблемы следующие:

1) Доступ к приватным переменным. Я хочу передать один из двух параметров List<long> в мое событие DoWork, но выполнить другой запрос в зависимости от того, какой список ему передан. Я могу обойти это, объявив частную логическую переменную уровня класса, которая действует как своего рода флаг. Это глупо спрашивать, но в моем DoWork, мне разрешен доступ к этой закрытой переменной и соответствующая маршрутизация запроса? (Я проверил это, и оно работает, без каких-либо ошибок)

private bool SearchEngaged = false;

private void bgw_DoWork(object sender, DoWorkEventArgs e) {
    BackgroundWorker worker = sender as BackgroundWorker;
    e.Result = GetTasks((List<long>)e.Argument, worker, e);
}
SortableBindingList<Task> GetTasks(List<long> argsList, BackgroundWorker worker, DoWorkEventArgs e) {
    SortableBindingList<Task> sbl = null;
    if (worker.CancellationPending) {
        e.Cancel = true;
    }
    else {
        if (SearchEngaged) {
            sbl = DU.GetTasksByKeys(argsList);
        }
        else {
            sbl = DU.GetTasksByDivision(argsList);
        }
    }
    return sbl;
}

2) Поток пользовательского интерфейса зависает при запуске RunWorkerCompleted. Хорошо, я знаю, что мой пользовательский интерфейс реагирует на событие DoWork, потому что для запуска и возврата моего SortableBindingList<Task>, если я не связываю Список с сеткой, требуется +/- 2 секунды, но просто заселить его. Однако мой пользовательский интерфейс зависает, когда я связываю это с сеткой, что я делаю в событии RunWorkerCompleted. Имейте в виду, что моя сетка имеет 4 столбца изображений, которые я обрабатываю в CellFormatting. Этот процесс занимает дополнительно 8 секунд, в течение которых мой пользовательский интерфейс полностью неинтерактивен. Я знаю о междисциплинарных последствиях этого, но есть ли какой-нибудь способ, которым я могу выполнить заполнение сетки и форматирование в фоновом режиме или без остановки моего пользовательского интерфейса ? RunWorkeCompleted выглядит так:

private void bgw_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e) {
    if (e.Cancelled) {
        lblStatus.Text = "Operation was cancelled";
    }
    else if (e.Error != null) {
        lblStatus.Text = string.Format("Error: {0}", e.Error.Message);
    }
    else {
        SortableBindingList<Task> sblResult = (SortableBindingList<Task>)e.Result;
        dgv.DataSource = sblResult;
        dgv.Enabled = true;
        TimeSpan Duration = DateTime.Now.TimeOfDay - DurationStart;
        lblStatus.Text = string.Format("Displaying {0} {1}", sblResult.Count, "Tasks");
        lblDuration.Visible = true;
        lblDuration.Text = string.Format("(data retrieved in {0} seconds)", Math.Round(Duration.TotalSeconds, 2));
        cmdAsyncCancel.Visible = false;
        tmrProgressUpdate.Stop();
        tmrProgressUpdate.Enabled = false;
        pbStatus.Visible = false;
    }
}

Извините за длинный запрос, но я буду очень признателен за ваши ответы! спасибо!

Ответы [ 2 ]

1 голос
/ 26 ноября 2009

Я думаю, что самое простое решение вашей проблемы - установить источник данных вашей сетки в DoWork вместо RunWorkerCompleted, используя Dispatcher.BeginInvoke, который вы упомянули сами. Примерно так:

private bool SearchEngaged = false;

private void bgw_DoWork(object sender, DoWorkEventArgs e)
{
    BackgroundWorker worker = sender as BackgroundWorker;
    SortableBindingList<Task> sblResult = GetTasks((List<long>)e.Argument, worker, e);

    BeginInvoke((Action<object>)(o => dataGridView1.DataSource = o), sblResult);
}

private void bgw_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
{
    if (e.Cancelled) {
        lblStatus.Text = "Operation was cancelled";
    }
    else if (e.Error != null) {
        lblStatus.Text = string.Format("Error: {0}", e.Error.Message);
    }
    else
    {
        dgv.Enabled = true;
        TimeSpan Duration = DateTime.Now.TimeOfDay - DurationStart;
        lblStatus.Text = string.Format("Displaying {0} {1}", sblResult.Count, "Tasks");
        lblDuration.Visible = true;
        lblDuration.Text = string.Format("(data retrieved in {0} seconds)", Math.Round(Duration.TotalSeconds, 2));
        cmdAsyncCancel.Visible = false;
        tmrProgressUpdate.Stop();
        tmrProgressUpdate.Enabled = false;
        pbStatus.Visible = false;
    }
}

Что касается проблемы с частными переменными, я не думаю, что она будет иметь какую-либо проблему в вашем случае. Если вы изменяете его с помощью какого-либо события пользовательского интерфейса, просто пометьте личное поле как volatile. Документацию по ключевому слову volatile можно найти здесь:

http://msdn.microsoft.com/en-us/library/x13ttww7.aspx

1 голос
/ 26 ноября 2009

Похоже, ваш код работает правильно.

Что касается 8 секунд, которые требуются потоку пользовательского интерфейса для обновления экрана, вы ничего не можете с этим поделать. Смотрите мой ответ на этот вопрос .

Чтобы оптимизировать часть пользовательского интерфейса, вы можете попробовать вызвать SuspendLayout и ResumeLayout на сетке или содержащей ее панели.

Вы также можете попытаться уменьшить объем обработки, выполняемой во время привязки данных. Например:

  • Расчеты, выполненные в сетке, можно перенести в модель данных (таким образом, делая их в рабочем потоке).
  • Если сетка автоматически вычисляет свои столбцы на основе модели данных, вместо этого попробуйте жестко их кодировать.
  • РЕДАКТИРОВАТЬ: Пейджинг данных в бизнес-уровне и заставить сетку показывать только небольшое количество строк за раз.
...