Следующий шаг: кнопка Show Count
Вполне вероятно, что в некоторых случаях этот код будет работать. Тем не менее:
- Взаимодействие вторичного потока с потоком, создающим графический интерфейс, не удается организовать очевидными средствами.
- Никогда не изменяйте элементы в графических программах из других программных потоков. Все изменения должны происходить только в потоке, создавшем графический интерфейс.
Если вы нарушите эти правила, мы гарантируем, что в ваших многопоточных графических программах будут возникать тонкие, неуловимые ошибки.
Примечание
Организовать взаимодействие объектов с применением событий тоже не удастся. 06-работник события выполняется в том же потоке, в котором произошел вызов RaiseEvent поэтому события вам не помогут.
И все же здравый смысл подсказывает, что в графических приложениях должны существовать средства модификации элементов из другого потока. В .NET Framework существует поточно-безопасный способ вызова методов приложений GUI из другого потока. Для этой цели используется особый тип делегатов Method Invoker из пространства имен System.Windows.Forms. В следующем фрагменте приведен новый вариант метода GetEes:
Private Sub GetEes() Dim I As Integer For I = 0 To m_length If m_Data.Chars(I) = CChar("E")Then m_count += 1 End If Next m_CountDone = True Try Dim mylnvoker As New Methodlnvoker(AddressOf UpDateButton) myInvoker.Invoke() Catch e As ThreadlnterruptedException 'Неудача End Try End Sub Public Sub UpDateButton() m_Button.Enabled =True End Sub
Межпоточные обращения к кнопке осуществляются не напрямую, а через Method Invoker. .NET Framework гарантирует, что этот вариант безопасен по отношению к потокам.
Почему при многопоточном программировании возникает столько проблем?
Теперь, когда вы получили некоторое представление о многопоточном программировании и о потенциальных проблемах, с ним связанных, мы решили, что в конце этой главы будет уместно ответить на вопрос, вынесенный в заголовок подраздела.
Одна из причин заключается в том, что многопоточность – процесс нелинейный, а мы привыкли к линейной модели программирования. На первых порах трудно привыкнуть к самой мысли о том, что выполнение программы может прерываться случайным образом, а управление будет передаваться другому коду.
Однако существует и другая, более фундаментальная причина: в наши дни программисты слишком редко программируют на ассемблере или хотя бы просматривают дизассемблированные результаты работы компилятора. Иначе им было бы гораздо проще привыкнуть к мысли, что одной команде языка высокого уровня (такого, как VB.NET) могут соответствовать десятки ассемблерных инструкций. Поток может прерываться после любой из этих инструкций, а следовательно – и посреди команды высокого уровня.
Но и это не все: современные компиляторы оптимизируют быстродействие программ, а оборудование компьютера может вмешиваться в процесс управления памятью. Как следствие, компилятор или оборудование может без вашего ведома изменить порядок команд, указанный в исходном тексте программы [Многие компиляторы оптимизируют циклические операции копирования массивов вида for i=0 to n:b(i)=a(i):ncxt. Компилятор (или даже специализированное устройство управления памятью) может просто создать массив, а потом заполнить его одной операцией копирования вместо многократного копирования отдельных элементов!].
Надеемся, эти пояснения помогут вам лучше понять, почему многопоточное программирование порождает столько проблем, – или по крайней мере меньше удивляться при виде странного поведения ваших многопоточных программ!