Создание потоков
Начнем с элементарного примера. Допустим, вы хотите запустить в отдельном потоке процедуру, которая в бесконечном цикле уменьшает значение счетчика. Процедура определяется в составе класса:
Public Class WillUseThreads Public Sub SubtractFromCounter() Dim count As Integer Do While True count -= 1 Console.WriteLlne("Am in another thread and counter =" & count) Loop End Sub End Class
Поскольку условие цикла Do остается истинным всегда, можно подумать, что ничто не помешает выполнению процедуры SubtractFromCounter. Тем не менее в многопоточном приложении это не всегда так.
В следующем фрагменте приведена процедура Sub Main, запускающая поток, и команда Imports:
Option Strict On Imports System.Threading Module Modulel Sub Main() 1 Dim myTest As New WillUseThreads() 2 Dim bThreadStart As New ThreadStart(AddressOf _ myTest.SubtractFromCounter) 3 Dim bThread As New Thread(bThreadStart) 4 ' bThread.Start() Dim i As Integer 5 Do While True Console.WriteLine("In main thread and count is " & i) i += 1 Loop End Sub End Module
Давайте последовательно разберем наиболее принципиальные моменты. Прежде всего процедура Sub Main всегда работает в главном потоке (main thread). В програм-мах .NET всегда работают минимум два потока: главный и поток сборки мусора. В строке 1 создается новый экземпляр тестового класса. В строке 2 мы создаем делегат ThreadStart и передаем адрес процедуры SubtractFromCounter экземпляра тестового класса, созданного в строке 1 (эта процедура вызывается без параметров).
Благодаря импортированию пространства имен Threading длинное имя можно не указывать. Объект нового потока создается в строке 3. Обратите внимание на передачу делегата ThreadStart при вызове конструктора класса Thread. Некоторые программисты предпочитают объединять эти две строки в одну логическую строку:
Dim bThread As New Thread(New ThreadStarttAddressOf _ myTest.SubtractFromCounter))
Наконец, строка 4 "запускает" поток, для чего вызывается метод Start экземпляра класса Thread, созданного для делегата ThreadStart. Вызывая этот метод, мы указываем операционной системе, что процедура Subtract должна работать в отдельном потоке.
Примечание
Слово "запускает" в предыдущем абзаце заключено в кавычки, поскольку в этом случае наблюдается одна из многих странностей многопоточного программирования: вызов Start не приводит к фактическому запуску потока! Он всего лишь сообщает, что операционная система должна запланировать выполнение указанного потока, но непосредственный запуск находится вне контроля программы. Вам не удастся начать выполнение потоков по своему усмотрению, потому что выполнением потоков всегда распоряжается операционная система. В одном из дальнейших разделов вы узнаете, как при помощи приоритета заставить операционную систему побыстрее запустить ваш поток.
На рис. 10.1 показан пример того, что может произойти после запуска программы и ее последующего прерывания клавишей CTRL + Break. В нашем случае новый поток запустился лишь после того, как счетчик в главном потоке увеличился до 341!
Рис. 10.1. Простая многопоточная программно время работы