Версия для печати
Нажмите сюда для просмотра этой темы в оригинальном формате |
Форум на Исходниках.RU > .NET FAQ > Разнопоточный доступ к GUI |
Автор: PIL 03.10.06, 10:36 |
По следам недавних вопросов на форуме, и в связи с неосвещенностью вопроса в FAQ родилась эта небольшая статья: Создан тестовый проект (VS2005) со следующим функционалом: - при нажатии на кнопку Start создается отдельный поток, который в цикле записывает данные в базу данных (в данном примере использовался Access). - после каждого добавления записи, при успешности операции - двигается прогресс бар для индикации процесса заливки записей. - после завершения заливки данных разблокируется кнопка Start для возможности повторить еще раз процесс заливки данных. Код обработчика кнопки старта процесса: <{CODE_COLLAPSE_OFF}><{CODE_WRAP_OFF}> Я специально использовал ParameterizedThreadStart для того, чтобы показать, как создаются потоки с передачей параметров.private void button1_Click(object sender, EventArgs e) { //Control.CheckForIllegalCrossThreadCalls = false; button1.Enabled = false; Thread th = new Thread(new ParameterizedThreadStart(DoGenerateRecords)); th.Start(connectionString); } Код рабочего метода потока: <{CODE_COLLAPSE_OFF}><{CODE_WRAP_OFF}> Если раскоментировать строку progressBar1.Value ++; в рабочем методе потока, приложение заработает, проблем не будет, но: если только попробовать запустить Debug, мы получим эксепшн:/// <summary> /// process generate records to DB /// </summary> private void DoGenerateRecords(object param) { OleDbConnection conn = new OleDbConnection((string)param); OleDbCommand cmd = new OleDbCommand("ADD_UNIT", conn); cmd.CommandType = CommandType.StoredProcedure; cmd.Parameters.Add("@name", OleDbType.Char, 50); cmd.Parameters.Add("@note", OleDbType.Char, 50); int realCount = 0; try { conn.Open(); for (int i = 0; i < COUNT; i++) { cmd.Parameters["@name"].Value = "some name " + i.ToString(); cmd.Parameters["@note"].Value = "some note " + i.ToString(); if (cmd.ExecuteNonQuery() > 0) { realCount++; //не правильный доступ. //progressBar1.Value ++; //правильный доступ if (onProgress != null) onProgress(realCount); } } } catch (Exception ex) { MessageBox.Show(ex.Message); } finally { if (conn.State == ConnectionState.Open) conn.Close(); if (onEndProcess != null) onEndProcess(); } } Cross thread operation not valid:....., это среда разработки предупреждает о том, что возможны ошибки при доступе к GUI из другого потока. Для ленивых программистов - эту генерацию исключений можно отключить - <{CODE_COLLAPSE_OFF}><{CODE_WRAP_OFF}> Control.CheckForIllegalCrossThreadCalls = false; , но как говорил Винни-Пух, "Нужно делать так как нужно, а так как не нужно делать не нужно" Дальше: Для добавления записей использовался хранимый запрос ADD_UNIT (синтаксис можно посмотреть в аттаче). Для сигнализации окончания процесса и прогресса использовались делегаты: <{CODE_COLLAPSE_OFF}><{CODE_WRAP_OFF}> delegate void AddProgressEventHandler(int val); delegate void EndPocessEventHandler(); // private event AddProgressEventHandler onProgress; private event EndPocessEventHandler onEndProcess; И собственно сам код подписчиков событий: <{CODE_COLLAPSE_OFF}><{CODE_WRAP_OFF}> void Form1_onEndProcess() { if (button1.InvokeRequired) { this.BeginInvoke( new EndPocessEventHandler(Form1_onEndProcess)); } else button1.Enabled = true; } void Form1_onProgress(int val) { if (progressBar1.InvokeRequired) { this.BeginInvoke( new AddProgressEventHandler(Form1_onProgress), new object[] { val }); } else progressBar1.Value = val; } свойство InvokeRequired элемента управления возвращает true, если достут к элементу управления осуществляется с другого потока. Методы Invoke/BeginInvoke формы позволяют выполнить метод в потоке GUI формы. При этом Invoke - синхронно, BeginInvoke - ассинхронно. Все, разрулили, теперь и студия не бросается исключениями, и на сердце спокойней Спасибо тем, кто дочитал до конца, в аттаче - рабочий проект (VS2005). Жду критики и коментариев. |
Автор: PIL 10.10.06, 13:20 |
Пришло письмо от робота, что архив битый, проверил, все нормально, но на всякий случай выкладываю еще раз: |
Автор: ANDLL 11.09.07, 16:53 |
Некорректность(нельзя не отметить, очень типичная): После BeginInvoke должен идти EndInvoke, обязательно. Архивы(оба) действительно битые, по крайней мере в explorer(vista) не открываются |
Автор: GazOn 20.09.07, 06:39 |
Если архив распаковать WinRar'ом, то там лежит один файл с неизвестным расширением. |
Автор: hinews 30.11.07, 17:46 |
Просто внутри архива лежит еще один архив. Так добавьте к этому файлу расширение .zip и будет тот самый архив. |
Автор: PIL 11.03.08, 09:52 |
- Нет, не обязательно, читаем Дж. Рихтера CLR via С# 2005 - стр. 604, - для GUI - это единственное исключение из правил, когда EndInvoke не обязателен |
Автор: Alexus 21.03.08, 10:25 |
Предвосхищая аналогичные вопросы по WPF - там Invoke можно найти в объекте типа System.Windows.Threading.Dispatcher. Ссылку на объект этого типа держит само приложение (System.Windows.Application.Current.Dispatcher), а так же все объекты, наследованные от System.Windows.Threading.DispatcherObject (это все контролы, окна, и даже все объект, кторые имеют зависимыс свойства(DependencyProperties)). |
Автор: mr.Torrens 29.01.10, 11:58 |
А если используется несколько потоков и из них необходимо править разные свойства десятка объектов(button, progress bar, listview), то для каждого надо прописывать такие события? Нет какого-то универсального решения? |
Автор: juice 31.01.10, 19:40 |
Асинхронное программирование может чем то поможет. |
Автор: PIL 01.02.10, 08:09 |
Цитата mr.Torrens @ - BackgroundWorker, SyncronizationContext. У меня на блоге немного инфы есть А если используется несколько потоков и из них необходимо править разные свойства десятка объектов(button, progress bar, listview), то для каждого надо прописывать такие события? Нет какого-то универсального решения? |
Автор: Raistlin 06.02.10, 19:17 |
Только что искал информацию по данной тематике для WPF. На первых строчках поисковика данная статья и еще вот эта: http://msdn.microsoft.com/ru-ru/magazine/cc163328.aspx?ppud=4 Нашел все, что было нужно |