C #: заполнение пользовательского интерфейса с использованием отдельных потоков - PullRequest
3 голосов
/ 10 мая 2010

Я пытаюсь найти смысл в приложении, которое мне передали, чтобы отследить источник ошибки. Есть немного кода (здесь упрощенно), который создает четыре потока, которые в свою очередь заполняют представления списка в главной форме. Каждый метод получает данные из базы данных и извлекает графику из библиотеки ресурсов для непосредственного заполнения списка изображений и списка просмотра.

Из того, что я прочитал здесь ( ссылка ), обновление элементов пользовательского интерфейса из любого потока, кроме потока пользовательского интерфейса, делать не следует, и все же это работает?

Thread t0 = new Thread(new ThreadStart(PopulateListView1));
t0.IsBackground = true;
t0.Start();

Thread t1 = new Thread(new ThreadStart(PopulateListView2));
t1.Start();

Thread t2 = new Thread(new ThreadStart(PopulateListView3));
t2.Start();

Thread t3 = new Thread(new ThreadStart(PopulateListView4));
t3.Start();

Сама ошибка - исключение System.InvalidOperationException «Невозможно добавить изображение в ImageList». который заставляет меня задуматься, связан ли вышеуказанный код каким-либо образом.

Рекомендуется ли этот метод заполнения пользовательского интерфейса, и если нет, каковы возможные осложнения в результате этого?

Обновление:

Возможно, я дал некоторую дезинформацию, ссылаясь на «форму». Приложение представляет собой приложение Windows Forms, но код взят из плагина приложения, основанного на пользовательском контроле. Потоки создаются внутри метода инициализации, публично предоставляемого этим элементом управления. Списки просмотра и т. Д. Также являются частью пользовательского контроля над этим плагином.

Ответы [ 5 ]

6 голосов
/ 10 мая 2010
  • НЕ ИСПОЛЬЗУЙТЕ потоки для этого - если вам нужно сделать эту асинхронность, используйте WOrkItems на THreadPool. Использование потоков в общем случае должно быть восстановлено для долго выполняющихся элементов - для этого лучше подходят THreadPool или новый API задач .NET 4.0.

  • Элементы пользовательского интерфейса должны быть только namipulated из потока создания элемента. Он «работает» или нет, в зависимости от того, какую версию .net Framework вы используете или какой элемент управления на самом деле используется, если вы нарушаете это.

3 голосов
/ 10 мая 2010

В ваших методах потоков, например, DoWork () для класса BackgroundWorker , например, вам потребуется метод делегата для заполнения элемента управления пользовательского интерфейса. Затем, проверяя, требуется ли вызывать ваш элемент управления пользовательским интерфейсом (свойство InvokeRequired ), затем вызывайте его, когда он требуется.

private delegate IList<MyObject> PopulateUiControl();

private void myThread_DoWork(object sender, DoWorkEventArgs e) {
    PopulateUiControl myDelegate = FillUiControl;

    while(uiControl.InvokeRequired)
        uiControl.Invoke(myDelegate);
}

private IList<MyObject> FillUiControl() {
    uiControl.Items = myThreadResultsITems;
}

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

В конце концов, я согласен с другими, стараюсь избегать таких вещей в будущем, так как это может быть сложно отладить или выявить какое-то странное поведение. Может быть, .NET 4 имеют некоторые улучшения по этой теме, как Microsoft работала трудно сделать параллелизм легким для использования многоядерных процессоров для разработчиков.

3 голосов
/ 10 мая 2010

Как уже говорили другие, вы не можете обновить свой пользовательский интерфейс из какого-либо потока, кроме того, в котором он был создан.

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

PS: Я предполагаю, что когда вы говорите UI, вы имеете в виду WindowsForms, а не WPF. Я успешно использовал вышеуказанное решение в WinForms.

Обновление: вот пара статей, которые подробно объясняют концепцию: * Потоки в Windows Forms * Вызывается поток пользовательского интерфейса WinForms: углубленное рассмотрение Invoke / BeginInvoke / InvokeRequred

Кроме того, связанный вопрос от SO: В WinForms, почему вы не можете обновить элементы управления пользовательского интерфейса из других потоков?

2 голосов
/ 10 мая 2010

Никогда не обновляйте пользовательский интерфейс из рабочего потока. Программа может иногда работать, но это неопределенное поведение. Добавьте эту строку в код инициализации программы:

Control.CheckForIllegalCrossThreadCalls = true;

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

0 голосов
/ 10 мая 2010

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

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...