Надлежащий способ передать значения GUI фоновому работнику? - PullRequest
3 голосов
/ 23 октября 2010

Я работаю с довольно сложным графическим интерфейсом и пытаюсь передать много данных из графического интерфейса в backgroudWorker. Проблема, с которой я сталкиваюсь, - это доступ к некоторым значениям GUI из фонового рабочего. Например, если я пытаюсь получить ComboBox.Text, я получаю InvalidOperationException из-за многопоточности. Однако, если я скажу do TextBox.Text, кажется, все работает нормально. Конечно, я довольно новичок в C #, поэтому мне немного непонятно, почему некоторые из них в порядке, а другие терпят неудачу.

Я придумала несколько способов решения своих проблем, но ищу лучшую практику у человека, имеющего опыт работы с c #.

Вот несколько способов исправить это

  1. создайте класс / структуру всех значений, которые вы хотите передать фоновому работнику, и передайте это при вызове RunworkAsync. Я не нашел это очень привлекательным, поскольку мне нужно было создать класс / структуру для каждой страницы в моем GUI, чтобы передать ее в backgroundWorker

  2. Создайте группу различных фоновых работников, у которых была определенная задача. У меня все еще были некоторые проблемы с передачей данных, но объем данных, которые я должен был передать, был значительно сокращен. Однако число DoWork / ProgressChanged / RunworkerCompleted значительно возросло, что оказалось далеко не идеальным.

  3. (это привело меня к тому, что я сейчас делаю)

создать делегата и метод для сбора информации

private delegate string ReadComboDelegate(ComboBox c);

private string ReadComboBox(ComboBox c)
{
    if(c.InvokeRequired)
    {
        ReadComboDelegate del = new ReadComboDelegate(this.ReadComboBox);
        return (string) c.Invoke(del,c);
    }
    else
    {
        return c.Text
    }
}

затем в DoWork сделайте что-то вроде string txt = this.ReadComboBox(this.comboBox1);

Когда у вас простой графический интерфейс и вам не нужно передавать много данных, это довольно простая проблема. Тем не менее, чем больше элементов и сложных GUI становится, тем больше становится эта проблема. Если у кого-то есть какая-либо информация, которая облегчит это, я был бы признателен.

Спасибо

Ответы [ 3 ]

2 голосов
/ 23 октября 2010

Проблема с перекрестными потоками, с которой вы сталкиваетесь, связана с требованием, чтобы только поток пользовательского интерфейса мог «касаться» элементов управления пользовательского интерфейса.

Я думаю, что наиболее согласованный метод передачи данных фоновому работнику - это ваше решение № 1 - создайте простую структуру, которая содержит все данные, необходимые для выполнения обработки.

Это намного проще, чем создание методов ReadXXX для каждого элемента управления в пользовательском интерфейсе, и он определяет, что фоновому процессу необходимо для выполнения своей задачи ...

1 голос
/ 23 октября 2010

Скорее всего, TextBox не вызывает этого исключения. Его свойство Text кэшируется в строку. Это не относится к ComboBox.Text и большинству других свойств элемента управления, он запрашивает собственный элемент управления Windows, и в этот момент Windows Forms обнаруживает, что вы пытаетесь использовать элемент управления из потока, отличного от потока пользовательского интерфейса. Не могу сделать.

Вам определенно нужно подумать о способе реструктуризации этого кода. Это не только незаконно, это невероятно дорого и принципиально небезопасно, поскольку пользовательский интерфейс может обновляться во время работы вашего работника. Соберите информацию из необходимых элементов управления в небольшой вспомогательный класс, передайте ее в качестве аргумента перегрузке RunWorkerAsync (объект). И вернуть его в DoWork от e.Argument.

0 голосов
/ 23 октября 2010

Я бы определенно избегал # 3. Несмотря на усердие в использовании Control.Invoke для координации рабочего потока и потоков пользовательского интерфейса, оно часто используется слишком часто и в лучшем случае является обычно неоптимальной стратегией. Я предпочитаю № 1 и № 2, а не № 3. Вот причины, по которым я стараюсь избегать # 3.

  • Он тесно связывает пользовательский интерфейс и рабочие потоки.
  • Рабочий поток получает возможность определять объем работы, выполняемой потоком пользовательского интерфейса.
  • Рабочий поток должен дождаться ответа потока пользовательского интерфейса, прежде чем продолжить.
  • Это дорогостоящая операция.

Я знаю, что это может потребовать дополнительных предварительных усилий с вашей стороны, чтобы запустить # 1 или # 2, но конечный результат будет лучше в долгосрочной перспективе.

Как следствие моего ответа, метод Control.Invoke имеет тенденцию к чрезмерному использованию, когда данные должны следовать в противоположном направлении (из рабочего потока в поток пользовательского интерфейса, как в случае отправки информации о ходе выполнения в пользовательский интерфейс). К сожалению, это метод, который BackgroundWorker использует внутри своего метода ReportProgress. обычно лучше, чтобы поток пользовательского интерфейса опрашивал общую структуру данных для этой информации по тем же причинам, что и выше, плюс:

  • Поток пользовательского интерфейса определяет, когда и как часто должно происходить обновление.
  • Он возлагает ответственность за обновление потока пользовательского интерфейса на поток пользовательского интерфейса, к которому он все равно должен принадлежать.
  • Нет риска переполнения насоса сообщений пользовательского интерфейса, как в случае с методами маршалинга, инициированными рабочим потоком.

Тем не менее, с учетом сказанного я не предлагаю полностью отказаться от BackgroundWorker. Просто помните некоторые из этих моментов.

...