Наши проекты:
Журнал · Discuz!ML · Wiki · DRKB · Помощь проекту |
||
ПРАВИЛА | FAQ | Помощь | Поиск | Участники | Календарь | Избранное | RSS |
[98.81.24.230] |
|
Сообщ.
#1
,
|
|
|
По следам недавних вопросов на форуме, и в связи с неосвещенностью вопроса в FAQ родилась эта небольшая статья:
Создан тестовый проект (VS2005) со следующим функционалом: - при нажатии на кнопку Start создается отдельный поток, который в цикле записывает данные в базу данных (в данном примере использовался Access). - после каждого добавления записи, при успешности операции - двигается прогресс бар для индикации процесса заливки записей. - после завершения заливки данных разблокируется кнопка Start для возможности повторить еще раз процесс заливки данных. Код обработчика кнопки старта процесса: private void button1_Click(object sender, EventArgs e) { //Control.CheckForIllegalCrossThreadCalls = false; button1.Enabled = false; Thread th = new Thread(new ParameterizedThreadStart(DoGenerateRecords)); th.Start(connectionString); } Код рабочего метода потока: /// <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 из другого потока. Для ленивых программистов - эту генерацию исключений можно отключить - Control.CheckForIllegalCrossThreadCalls = false; , но как говорил Винни-Пух, "Нужно делать так как нужно, а так как не нужно делать не нужно" Дальше: Для добавления записей использовался хранимый запрос ADD_UNIT (синтаксис можно посмотреть в аттаче). Для сигнализации окончания процесса и прогресса использовались делегаты: delegate void AddProgressEventHandler(int val); delegate void EndPocessEventHandler(); // private event AddProgressEventHandler onProgress; private event EndPocessEventHandler onEndProcess; И собственно сам код подписчиков событий: 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). Жду критики и коментариев. Прикреплённый файлGUI_MultithreadTest.zip (43.64 Кбайт, скачиваний: 669) |
Сообщ.
#2
,
|
|
|
Пришло письмо от робота, что архив битый, проверил, все нормально, но на всякий случай выкладываю еще раз:
Прикреплённый файлGUI_MultithreadTest.zip (43.64 Кбайт, скачиваний: 681) |
Сообщ.
#3
,
|
|
|
Некорректность(нельзя не отметить, очень типичная): После BeginInvoke должен идти EndInvoke, обязательно. Архивы(оба) действительно битые, по крайней мере в explorer(vista) не открываются
|
Сообщ.
#4
,
|
|
|
Если архив распаковать WinRar'ом, то там лежит один файл с неизвестным расширением.
|
Сообщ.
#5
,
|
|
|
Просто внутри архива лежит еще один архив. Так добавьте к этому файлу расширение .zip и будет тот самый архив.
|
Сообщ.
#6
,
|
|
|
Цитата ANDLL @ - Нет, не обязательно, читаем Дж. Рихтера CLR via С# 2005 - стр. 604, - для GUI - это единственное исключение из правил, когда EndInvoke не обязателен После BeginInvoke должен идти EndInvoke, обязательно. |
Сообщ.
#7
,
|
|
|
Предвосхищая аналогичные вопросы по WPF - там Invoke можно найти в объекте типа System.Windows.Threading.Dispatcher. Ссылку на объект этого типа держит само приложение (System.Windows.Application.Current.Dispatcher), а так же все объекты, наследованные от System.Windows.Threading.DispatcherObject (это все контролы, окна, и даже все объект, кторые имеют зависимыс свойства(DependencyProperties)).
|
Сообщ.
#8
,
|
|
|
А если используется несколько потоков и из них необходимо править разные свойства десятка объектов(button, progress bar, listview), то для каждого надо прописывать такие события? Нет какого-то универсального решения?
|
Сообщ.
#9
,
|
|
|
Асинхронное программирование может чем то поможет.
|
Сообщ.
#10
,
|
|
|
Цитата mr.Torrens @ - BackgroundWorker, SyncronizationContext. У меня на блоге немного инфы есть А если используется несколько потоков и из них необходимо править разные свойства десятка объектов(button, progress bar, listview), то для каждого надо прописывать такие события? Нет какого-то универсального решения? |
Сообщ.
#11
,
|
|
|
Только что искал информацию по данной тематике для WPF. На первых строчках поисковика данная статья и еще вот эта:
http://msdn.microsoft.com/ru-ru/magazine/cc163328.aspx?ppud=4 Нашел все, что было нужно |