#c_sharp #многопоточность #gui
class test { public Listthreads = new List (); public int nThreads = 0; public int maxThreads = 5; public void DoWork(object data) { string message = (string)data; //MessageBox.Show(message); } public void CreateThread(object data) { if (nThreads >= maxThreads) return; Thread newThread = new Thread(DoWork); threads.Add(newThread); newThread.IsBackground = true; newThread.Start(data); nThreads++; } public void WindUpThreads() { //MessageBox.Show("count: " + nThreads.ToString()); for(int i = 0; i < threads.Count; i++) { if (threads[i].IsAlive == false) { threads[i].Abort(); threads.RemoveAt(i); //MessageBox.Show("removing at " + i.ToString()); } } nThreads = threads.Count; } } Мне нужно в методе DoWork изменять данные в listbox, но я не хочу привязывать логику к элементам управления. Как лучше поступить в таком случае? В общем я решил пойти самым простым путем и передал делегат в тред: public delegate void TestDeleg(string message); public class Data { public TestDeleg deleg; public string mess; } public void DoWork(object data) { Data d = (Data)data; d.deleg(d.mess); //string message = (string)data; //MessageBox.Show(d.mess); return; } В цикле теперь передаю класс с запакованным данными: Data d = new Data(); d.deleg = AddItem; d.mess = strings[counter]; thTest.CreateThread((object)d); Ну и сам метод, который добавляет строку в листбокс: public void AddItem(string message) { //listBox1.Items.Add(message); if(InvokeRequired) listBox1.Invoke( (Action)( () => { listBox1.Items.Add(message); } ) ); else listBox1.Items.Add(message); } Теперь почему-то цикл никогда не заканчивается и треды не завершаются, метод, который должен очищать треды почему-то не срабатывает. UPD: Кажется, причина, по которой интерфейс не обновлялся, выяснилась. Основная часть моей программы была такой: while (flag == true) { if (counter >= dataCount) { flag = false; } while (thTest.nThreads < thTest.maxThreads) { if (flag == false) break; thTest.CreateThread(strings[counter]); counter++; } thTest.WindUpThreads(); if (flag == false) { do { thTest.WindUpThreads(); } while (thTest.nThreads != 0); } } И запускался этот код по нажатию на кнопку. Получается, что цикл крутился в основном потоке и поэтому ui не принимал сообщения об изменении. Когда я вынес уже этот код в отдельный поток (получается один поток создает другие и управляет ими), то все стало на свои места.
Ответы
Ответ 1
Нужно вашу логику отделить от представления. Представить ваше "сердце" приложения как некоторую штуку, которая принимает "команды" из UI и отправляет информацию об изменении состояния - "события". В главном потоке слушать события UI и отправлять в "сердце" соответствующие им команды; подписаться на события основной логики и в соответствий с происходящим перерисовывать ваш UI. Посмотрите на IObservable и Reactive Extensions. И общие замечания - не рекомендуется создавать потоки руками. Поток - это достаточно тяжелая сущность. Многопоточность реализуется через различные библиотеки, где примитивы несколько интереснее и более удобные, чем потоки. Куда смотреть - Rx, Task Parrallel Library; читать - Jeffrey Richter, CLR via C# последнюю редакцию, главу про многопоточность.Ответ 2
Завести класс, хранящий состояние DoWork для отображения. Передать в DoWork делегат или объект presenter, который будет отображать состояние на listbox (используя BeginInvoke) Вызывать периодически presenter из под DoWorkОтвет 3
Как вариант, вы можете в потоках класть данные для ListBox в какую-нибудь коллекцию из System.Collections.Concurrent, а в основном (GUI) треде брать оттуда элементы и пихать их в целевой ListBox. Лично я предпочел бы использовать ConcurrentQueueследующим образом: В потоках добавляем элементы. В GUI-треде по таймеру забираем и очищаем. Ответ 4
Мне нужно в методе DoWork изменять данные в listbox Если метод DoWork вызывается из разных потоков, то это можно проверить и данные передать в UI-поток, примерно так: public void DoWork(object data) { if (listbox.InvokeRequired) listbox.BeginInvoke(new Action
Комментариев нет:
Отправить комментарий