Пошаговое руководство. Разработка простого многопоточного компонента с использованием Visual Basic

Обновлен: Ноябрь 2007

Компонент BackgroundWorker заменяет аналогичный код из пространства имен System.Threading и расширяет его функциональные возможности, но при необходимости исходное пространство имен System.Threading можно сохранить для обеспечения обратной совместимости и использования в будущем. Дополнительные сведения см. в разделе Общие сведения о компоненте BackgroundWorker.

Можно написать приложение, которое будет одновременно выполнять несколько задач. Эта возможность (называемая многопоточностью или созданием свободных потоков) является мощным средством разработки компонентов, для которых требуются значительные процессорные ресурсы и ввод данных пользователем. Примером компонента, в котором можно использовать многопоточность, служит компонент, производящий расчет заработной платы. Этот компонент может в одном потоке обрабатывать данные, введенные пользователем в базу данных, в то время как в другом потоке будут выполняться вычисления, потребляющие значительные ресурсы процессора. При запуске этих действий в отдельных потоках пользователю не нужно ждать, пока компьютер закончит вычисления, чтобы ввести следующие данные. В данном пошаговом руководстве создается простой многопоточный компонент, который выполняет одновременно несколько сложных вычислений.

Создание проекта

Приложение будет состоять из одной формы и компонента. Пользователь будет вводить значения и сообщать компоненту о необходимости начать вычисления. Форма будет получать из компонента значения и отображать их в элементах управления "Label". Компонент будет выполнять вычисления, занимающие процессор, и сообщать форме о завершении. Для хранения значений, полученных из интерфейса пользователя, в компоненте следует создать общие переменные. В компоненте следует также реализовать методы для выполнения вычислений на основе значений этих переменных.

xeh66hxs.alert_note(ru-ru,VS.90).gifПримечание.

Несмотря на то что в качестве метода, вычисляющего значение, обычно используется функция, аргументы между потоками передаваться не могут и значения не возвращаются. Существует множество простых способов передачи значений потокам и получения значений из них. В этом примере значения будут возвращаться в интерфейс пользователя путем обновления общих переменных, а для уведомления основной программы о завершении выполнения потока будут использоваться события.

Отображаемые диалоговые окна и команды меню могут отличаться от описанных в справке в зависимости от текущих параметров или выпуска среды. Для изменения параметров выберите Импорт и экспорт параметров в меню Сервис. Дополнительные сведения см. в разделе Параметры Visual Studio.

Чтобы создать форму, выполните следующие действия.

  1. Создайте новый проект Приложение Windows.

  2. Задайте для приложения имя Calculations и переименуйте форму Form1.vb в frmCalculations.vb.

  3. Получив приглашение от Visual Studio переименовать элемент кода Form1, нажмите кнопку Да.

    Эта форма будет служить в приложении основным интерфейсом пользователя.

  4. Добавьте в форму пять элементов управления Label, четыре элемента управления Button и один элемент управления TextBox.

    Элемент управления

    Имя

    Текст

    Label1

    lblFactorial1

    (пусто)

    Label2

    lblFactorial2

    (пусто)

    Label3

    lblAddTwo

    (пусто)

    Label4

    lblRunLoops

    (пусто)

    Label5

    lblTotalCalculations

    (пусто)

    Button1

    btnFactorial1

    Факториал

    Button2

    btnFactorial2

    Факториал - 1

    Button3

    btnAddTwo

    Прибавить два

    Button4

    btnRunLoops

    Выполнить цикл

    TextBox1

    txtValue

    (пусто)

Чтобы создать компонент "Калькулятор", выполните следующие действия.

  1. В меню Проект выберите Добавить компонент.

  2. Задайте компоненту имя Calculator.

Чтобы добавить в компонент "Калькулятор" общие переменные, выполните следующие действия.

  1. Откройте Редактор кода для компонента Calculator.

  2. Добавьте операторы для создания общих переменных, которые будут использоваться для передачи значений из формы frmCalculations в каждый поток.

    Переменная varTotalCalculations будет хранить текущее общее количество вычислений, выполненных компонентом, а остальные переменные будут получать значения из формы.

    Public varAddTwo As Integer
    Public varFact1 As Integer
    Public varFact2 As Integer
    Public varLoopValue As Integer
    Public varTotalCalculations As Double = 0
    

Чтобы добавить в компонент "Калькулятор" методы и события, выполните следующие действия.

  1. Объявите события, которые будут использоваться компонентом для передачи значений в форму. Сразу под объявлением переменных, сделанным на предыдущем шаге, введите следующий код.

    Public Event FactorialComplete(ByVal Factorial As Double, ByVal _
       TotalCalculations As Double)
    Public Event FactorialMinusComplete(ByVal Factorial As Double, ByVal _
       TotalCalculations As Double)
    Public Event AddTwoComplete(ByVal Result As Integer, ByVal _
       TotalCalculations As Double)
    Public Event LoopComplete(ByVal TotalCalculations As Double, ByVal _
       Counter As Integer)
    
  2. Непосредственно после объявления переменных, сделанного на шаге 1, введите следующий код.

    ' This sub will calculate the value of a number minus 1 factorial 
    ' (varFact2-1!).
    Public Sub FactorialMinusOne()
       Dim varX As Integer = 1
       Dim varTotalAsOfNow As Double
       Dim varResult As Double = 1
       ' Performs a factorial calculation on varFact2 - 1.
       For varX = 1 to varFact2 - 1
          varResult *= varX
          ' Increments varTotalCalculations and keeps track of the current
          ' total as of this instant.
          varTotalCalculations += 1
          varTotalAsOfNow = varTotalCalculations
       Next varX
       ' Signals that the method has completed, and communicates the 
       ' result and a value of total calculations performed up to this 
       ' point
       RaiseEvent FactorialMinusComplete(varResult, varTotalAsOfNow)
    End Sub
    
    ' This sub will calculate the value of a number factorial (varFact1!).
    Public Sub Factorial()
       Dim varX As Integer = 1
       Dim varResult As Double = 1
       Dim varTotalAsOfNow As Double = 0
       For varX = 1 to varFact1
           varResult *= varX
           varTotalCalculations += 1
           varTotalAsOfNow = varTotalCalculations
       Next varX
       RaiseEvent FactorialComplete(varResult, varTotalAsOfNow)
    End Sub
    
    ' This sub will add two to a number (varAddTwo + 2).
    Public Sub AddTwo()
       Dim varResult As Integer
       Dim varTotalAsOfNow As Double
       varResult = varAddTwo + 2
       varTotalCalculations += 1
       varTotalAsOfNow = varTotalCalculations
       RaiseEvent AddTwoComplete(varResult, varTotalAsOfNow)
    End Sub
    
    ' This method will run a loop with a nested loop varLoopValue times.
    Public Sub RunALoop()
       Dim varX As Integer
       Dim varY As Integer
       Dim varTotalAsOfNow As Double
       For varX = 1 To varLoopValue
          ' This nested loop is added solely for the purpose of slowing
          ' down the program and creating a processor-intensive
          ' application.
          For varY = 1 To 500
             varTotalCalculations += 1
             varTotalAsOfNow = varTotalCalculations
          Next
       Next
       RaiseEvent LoopComplete(varTotalAsOfNow, varX - 1)
    End Sub
    

Передача введенных пользователем данных в компонент

Следующим шагом является добавление в форму frmCalculations кода для получения введенных пользователем данных, а также для обмена значениями с компонентом Calculator.

Чтобы реализовать в форме "frmCalculations" интерфейс пользователя, выполните следующие действия.

  1. В меню Построение выберите Построить решение.

  2. Откройте форму frmCalculations в конструкторе Windows Forms.

  3. В Панели элементов перейдите на вкладку Компоненты вычислений. Перетащите компонент Калькулятор на поверхность разработки.

  4. В окне Свойства нажмите кнопку События.

  5. Дважды щелкните каждое из четырех событий, чтобы создать обработчики событий в форме frmCalculations. После создания каждого обработчика необходимо вернуться в конструктор.

  6. Чтобы обработать события, получаемые формой от компонента Calculator1, вставьте следующий код:

    Private Sub Calculator1_AddTwoComplete(ByVal Result As System.Int32, ByVal TotalCalculations As System.Double) Handles Calculator1.AddTwoComplete
        lblAddTwo.Text = Result.ToString
        btnAddTwo.Enabled = True
        lblTotalCalculations.Text = "TotalCalculations are " & _
            TotalCalculations.ToString
    End Sub
    
    Private Sub Calculator1_FactorialComplete(ByVal Factorial As System.Double, ByVal TotalCalculations As System.Double) Handles Calculator1.FactorialComplete
        ' Displays the returned value in the appropriate label.
        lblFactorial1.Text = Factorial.ToString
        ' Re-enables the button so it can be used again.
        btnFactorial1.Enabled = True
        ' Updates the label that displays the total calculations performed
        lblTotalCalculations.Text = "TotalCalculations are " & _
           TotalCalculations.ToString
    End Sub
    
    Private Sub Calculator1_FactorialMinusComplete(ByVal Factorial As System.Double, ByVal TotalCalculations As System.Double) Handles Calculator1.FactorialMinusComplete
        lblFactorial2.Text = Factorial.ToString
        btnFactorial2.Enabled = True
        lblTotalCalculations.Text = "TotalCalculations are " & _
            TotalCalculations.ToString
    End Sub
    
    Private Sub Calculator1_LoopComplete(ByVal TotalCalculations As System.Double, ByVal Counter As System.Int32) Handles Calculator1.LoopComplete
        btnRunLoops.Enabled = True
        lblRunLoops.Text = Counter.ToString
        lblTotalCalculations.Text = "TotalCalculations are " & _
           TotalCalculations.ToString
    End Sub
    
  7. В нижней части редактора кода найдите оператор End Class. Непосредственно над ним добавьте следующий код для обработки нажатий кнопок:

    Private Sub btnFactorial1_Click(ByVal sender As Object, ByVal e As _
       System.EventArgs) Handles btnFactorial1.Click
       ' Passes the value typed in the txtValue to Calculator.varFact1.
       Calculator1.varFact1 = CInt(txtValue.Text)
       ' Disables the btnFactorial1 until this calculation is complete.
       btnFactorial1.Enabled = False
       Calculator1.Factorial()
    End Sub
    
    Private Sub btnFactorial2_Click(ByVal sender As Object, ByVal e _
       As System.EventArgs) Handles btnFactorial2.Click
       Calculator1.varFact2 = CInt(txtValue.Text)
       btnFactorial2.Enabled = False
       Calculator1.FactorialMinusOne()
    End Sub
    
    Private Sub btnAddTwo_Click(ByVal sender As Object, ByVal e As _
       System.EventArgs) Handles btnAddTwo.Click
       Calculator1.varAddTwo = CInt(txtValue.Text)
       btnAddTwo.Enabled = False
       Calculator1.AddTwo()
    End Sub
    
    Private Sub btnRunLoops_Click(ByVal sender As Object, ByVal e As _
       System.EventArgs) Handles btnRunLoops.Click
       Calculator1.varLoopValue = CInt(txtValue.Text)
       btnRunLoops.Enabled = False
       ' Lets the user know that a loop is running.
       lblRunLoops.Text = "Looping"
       Calculator1.RunALoop()
    End Sub
    

Проверка работы приложения

На этом этапе создан проект, который содержит форму и компонент, предназначенный для выполнения некоторых сложных вычислений. Несмотря на то что многопоточность еще не реализована, сейчас следует проверить работоспособность проекта.

Чтобы проверить проект, выполните следующие действия.

  1. В меню Отладка выберите команду Начать отладку. Приложение начнет работу и появится форма frmCalculations.

  2. В текстовом поле введите 4, а затем нажмите кнопку с надписью Прибавить два.

    Под кнопкой должна появиться цифра "6", а в метке lblTotalCalculations должен быть отображен текст "Всего вычислений – 1".

  3. Теперь нажмите кнопку с надписью Факториал - 1.

    Под кнопкой должна появиться цифра "6", а метка lblTotalCalculations теперь содержит текст "Всего вычислений — 4".

  4. Измените значение текстового поля на 20, а затем нажмите кнопку с надписью Факториал.

    Под кнопкой должно появиться число "2.43290200817664E+18", а метка lblTotalCalculations теперь содержит текст "Всего вычислений — 24".

  5. Измените значение текстового поля на 50000, а затем нажмите кнопку с надписью Выполнить цикл.

    Заметьте, что перед тем, как кнопка вновь станет доступна, пройдет небольшой, но заметный интервал времени. В метке под кнопкой должно отображаться "50000", а общее количество вычислений теперь равно 25000024.

  6. Измените значение текстового поля на 50000, нажмите кнопку с надписью Выполнить цикл и непосредственно после этого нажмите кнопку Добавить два. Нажмите кнопку Добавить два еще раз.

    Кнопка не будет реагировать (так же как и все остальные элементы управления в форме), пока не завершится цикл.

    Если в программе запускается только один поток выполнения, занимающие процессор вычисления (аналогичные приведенным выше) могут приостанавливать программу, пока они не будут закончены. В следующем разделе приложение будет дополнено поддержкой многопоточности, в результате чего несколько потоков смогут выполняться одновременно.

Добавление поддержки многопоточности

В предыдущем примере были показаны ограничения, характерные для приложений, в которых запускается только один поток выполнения. В следующем разделе в компонент будет добавлено несколько потоков выполнения с помощью класса Thread.

Чтобы добавить подпрограмму "Threads", выполните следующие действия.

  1. Откройте файл Calculator.vb в Редакторе кода. В верхней части кода найдите строку Public Class Calculator . Сразу после нее введите следующий код:

    ' Declares the variables you will use to hold your thread objects.
    Public FactorialThread As System.Threading.Thread
    Public FactorialMinusOneThread As System.Threading.Thread
    Public AddTwoThread As System.Threading.Thread
    Public LoopThread As System.Threading.Thread
    
  2. Непосредственно перед оператором End Class в нижней части кода добавьте следующий метод:

    Public Sub ChooseThreads(ByVal threadNumber As Integer)
    ' Determines which thread to start based on the value it receives.
       Select Case threadNumber
          Case 1
             ' Sets the thread using the AddressOf the subroutine where
             ' the thread will start.
             FactorialThread = New System.Threading.Thread(AddressOf _
                Factorial)
             ' Starts the thread.
             FactorialThread.Start()
          Case 2
             FactorialMinusOneThread = New _
                System.Threading.Thread(AddressOf FactorialMinusOne)
             FactorialMinusOneThread.Start()
          Case 3
             AddTwoThread = New System.Threading.Thread(AddressOf AddTwo)
             AddTwoThread.Start()
          Case 4
             LoopThread = New System.Threading.Thread(AddressOf RunALoop)
             LoopThread.Start()
       End Select
    End Sub
    

    Когда будет создан экземпляр объекта Thread, ему потребуется аргумент в виде объекта ThreadStart. Объект ThreadStart — это делегат, указывающий на адрес подпрограммы, в которой начинается поток. Объект ThreadStart не может принимать параметры и передавать значения, и поэтому не может определять функцию. Оператор Оператор AddressOf возвращает объект-делегат, который служит объектом ThreadStart. Реализованная здесь подпрограмма ChooseThreads будет получать значение из вызывающей ее программы и использовать это значение для определения запускаемого потока.

Чтобы добавить в форму "frmCalculations" код запуска потока, выполните следующие действия.

  1. Откройте файл frmCalculations.vb в Редакторе кода. Найдите подпрограмму Sub btnFactorial1_Click.

    1. Преобразуйте строку, которая вызывает метод Calculator1.Factorial, в комментарий следующим образом.

      ' Calculator1.Factorial
      
    2. Добавьте следующую строку для вызова метода Calculator1.ChooseThreads.

      ' Passes the value 1 to Calculator1, thus directing it to start the ' correct thread.
      Calculator1.ChooseThreads(1)
      
  2. Внесите аналогичные изменения в другие подпрограммы button_click.

    xeh66hxs.alert_note(ru-ru,VS.90).gifПримечание.

    Убедитесь в том, что для аргумента threads задано соответствующее значение.

    В результате код должен выглядеть примерно следующим образом.

    Private Sub btnFactorial1_Click(ByVal sender As Object, ByVal e As _
       System.EventArgs) Handles btnFactorial1.Click
       ' Passes the value typed in the txtValue to Calculator.varFact1.
       Calculator1.varFact1 = CInt(txtValue.Text)
       ' Disables the btnFactorial1 until this calculation is complete.
       btnFactorial1.Enabled = False
       ' Calculator1.Factorial()
       ' Passes the value 1 to Calculator1, thus directing it to start the
       ' Correct thread.
       Calculator1.ChooseThreads(1)
    End Sub
    
    Private Sub btnFactorial2_Click(ByVal sender As Object, ByVal e As _
       System.EventArgs) Handles btnFactorial2.Click
       Calculator1.varFact2 = CInt(txtValue.Text)
       btnFactorial2.Enabled = False
       ' Calculator1.FactorialMinusOne()
       Calculator1.ChooseThreads(2)
    End Sub
    
    Private Sub btnAddTwo_Click(ByVal sender As Object, ByVal e As _
       System.EventArgs) Handles btnAddTwo.Click
       Calculator1.varAddTwo = CInt(txtValue.Text)
       btnAddTwo.Enabled = False
       ' Calculator1.AddTwo()
       Calculator1.ChooseThreads(3)
    End Sub
    
    Private Sub btnRunLoops_Click(ByVal sender As Object, ByVal e As _
       System.EventArgs) Handles btnRunLoops.Click
       Calculator1.varLoopValue = CInt(txtValue.Text)
       btnRunLoops.Enabled = False
       ' Lets the user know that a loop is running.
       lblRunLoops.Text = "Looping"
       ' Calculator1.RunALoop()
       Calculator1.ChooseThreads(4)
    End Sub
    

Маршалинг элементов управления

Теперь можно легко обновить отображение формы. Поскольку элементы управления всегда принадлежат главному потоку выполнения, любой вызов элемента управления из подчиненного потока требует обращения к процедуре маршалинга. Маршалинг — это процесс передачи вызова через границы потоков, который потребляет значительный объем ресурсов. Чтобы снизить до минимума объем необходимых операций маршалинга, а также устранить возможность возникновения конфликтов между потоками при обработке вызовов, следует использовать метод BeginInvoke для вызова методов в главном потоке выполнения. Такой способ вызова необходим при обращении к методам, работающим с элементами управления. Дополнительные сведения см. в разделе Практическое руководство. Управление элементами управления из потоков.

Чтобы создать процедуры для вызова элементов управления, выполните следующие действия.

  1. Откройте Редактор кода для компонента frmCalculations. В разделе объявлений добавьте следующий код.

    Public Delegate Sub FHandler(ByVal Value As Double, ByVal _
       Calculations As Double)
    Public Delegate Sub A2Handler(ByVal Value As Integer, ByVal _
       Calculations As Double)
    Public Delegate Sub LDhandler(ByVal Calculations As Double, ByVal _
       Count As Integer)
    

    В методах Invoke и BeginInvoke в качестве аргумента должен быть указан делегат для вызываемого метода. Эти строки объявляют подписи делегатов, которые будут использоваться методом BeginInvoke для вызова соответствующих методов.

  2. Добавьте в код следующие пустые методы.

    Public Sub FactHandler(ByVal Factorial As Double, ByVal TotalCalculations As _
       Double)
    End Sub
    Public Sub Fact1Handler(ByVal Factorial As Double, ByVal TotalCalculations As _
       Double)
    End Sub
    Public Sub Add2Handler(ByVal Result As Integer, ByVal TotalCalculations As _
       Double)
    End Sub
    Public Sub LDoneHandler(ByVal TotalCalculations As Double, ByVal Counter As _
       Integer)
    End Sub
    
  3. В меню Правка с помощью команд Вырезать и Вставить вырежьте весь код метода Sub Calculator1_FactorialComplete и вставьте его в метод FactHandler.

  4. Повторите предыдущий шаг для методов Calculator1_FactorialMinusComplete, Fact1Handler, Calculator1_AddTwoComplete, Add2Handler, Calculator1_LoopComplete и LDoneHandler.

    В результате в методах Calculator1_FactorialComplete, Calculator1_FactorialMinusComplete, Calculator1_AddTwoComplete и Calculator1_LoopComplete код остаться не должен. Он должен быть перемещен в соответствующие новые методы.

  5. Для асинхронного вызова методов вызовите метод BeginInvoke. Метод BeginInvoke можно вызвать либо из самой формы (me), либо из любого элемента управления в форме.

    Private Sub Calculator1_FactorialComplete(ByVal Factorial As System.Double, ByVal TotalCalculations As System.Double) Handles Calculator1.FactorialComplete
       ' BeginInvoke causes asynchronous execution to begin at the address
       ' specified by the delegate. Simply put, it transfers execution of 
       ' this method back to the main thread. Any parameters required by 
       ' the method contained at the delegate are wrapped in an object and 
       ' passed. 
       Me.BeginInvoke(New FHandler(AddressOf FactHandler), New Object() _
          {Factorial, TotalCalculations })
    End Sub
    
    Private Sub Calculator1_FactorialMinusComplete(ByVal Factorial As System.Double, ByVal TotalCalculations As System.Double) Handles Calculator1.FactorialMinusComplete
       Me.BeginInvoke(New FHandler(AddressOf Fact1Handler), New Object() _
          { Factorial, TotalCalculations })
    End Sub
    
    Private Sub Calculator1_AddTwoComplete(ByVal Result As System.Int32, ByVal TotalCalculations As System.Double) Handles Calculator1.AddTwoComplete
       Me.BeginInvoke(New A2Handler(AddressOf Add2Handler), New Object() _
          { Result, TotalCalculations })
    End Sub
    
    Private Sub Calculator1_LoopComplete(ByVal TotalCalculations As System.Double, ByVal Counter As System.Int32) Handles Calculator1.LoopComplete
       Me.BeginInvoke(New LDHandler(AddressOf Ldonehandler), New Object() _
          { TotalCalculations, Counter })
    End Sub
    

    Может показаться, что обработчик событий просто вызывает очередной метод. На самом деле, обработчик событий инициирует метод в главном потоке операций. Этот же подход сохраняется и для вызовов через границы потоков, и позволяет многопоточным приложениям работать эффективно, не вызывая блокировку. Подробные сведения о работе с этими элементами управления в многопоточной среде см. в разделе Практическое руководство. Управление элементами управления из потоков.

  6. Сохраните результаты работы.

  7. Проверьте решение, выбрав команду Начать отладку в меню Отладка.

    1. Введите в текстовое поле значение 10000000 и нажмите кнопку Выполнить цикл.

      В метке под кнопкой будет отображен текст "Выполнение цикла". Выполнение этого цикла занимает значительное количество времени. Если он завершается слишком рано, увеличьте число соответствующим образом.

    2. Быстро нажмите подряд все три кнопки, которые пока доступны. Все кнопки отреагируют на ввод данных. Первым должен появиться результат в метке под кнопкой Прибавить два. Следующими появятся результаты в метках под кнопками факториалов. Результатом в этих случаях будет бесконечность, поскольку число, возвращаемое при вычислении факториала для 10 000 000, слишком велико для хранения в переменной с двойной точностью. Затем после некоторой задержки появятся результаты под кнопкой с надписью Выполнить цикл.

      Таким образом, четыре отдельных группы вычислений были выполнены одновременно в четырех отдельных потоках. Интерфейс пользователя мог реагировать на ввод данных, и результаты возвращались после завершения работы каждого потока.

Координирование потоков

Опытный пользователь многопоточных приложений может заметить во введенном коде небольшие ошибки. Рассмотрим вновь следующие строки кода, имеющиеся в каждой подпрограмме вычислений в компоненте Calculator:

varTotalCalculations += 1
varTotalAsOfNow = varTotalCalculations

Эти две строки кода увеличивают общую переменную varTotalCalculations и присваивают ее значение локальной переменной varTotalAsOfNow. Это значение затем возвращается в форму frmCalculations и отображается в метке. Однако неизвестно, будет ли возвращено правильное значение. Если работает только один поток выполнения, то будет возвращено правильное значение. Однако если работают несколько потоков, правильность значения гарантировать нельзя. Каждый поток может увеличивать переменную varTotalCalculations. После того как один поток увеличит значение этой переменной, но перед тем как оно скопируется в varTotalAsOfNow, другой поток может также увеличить значение этой переменной. В итоге становится возможным, что каждый из потоков сообщит неточные результаты. В состав Visual Basic входит оператор Оператор SyncLock, который обеспечивает синхронизацию потоков. Это гарантирует точность результатов, возвращаемых каждым потоком. Синтаксис оператора SyncLock выглядит следующим образом.

SyncLock AnObject
   Insert code that affects the object
   Insert some more
   Insert even more
' Release the lock
End SyncLock

Если введен блок SyncLock, выполнение указанного выражения блокируется до тех пор, пока данный поток не снимет монопольную блокировку с рассматриваемого объекта. В приведенном выше примере выполнение блокируется для объекта AnObject. Оператор SyncLock следует применять к объекту, который возвращает ссылку, а не значение. Выполнение может затем продолжиться в виде блока, защищенного от воздействия со стороны других потоков. Набор операторов, которые выполняются как единый блок, называется атомарным. При появлении строки End SyncLock выражение освобождается и потоки могут продолжать работу.

Чтобы добавить в приложение оператор SyncLock, выполните следующие действия.

  1. Откройте файл Calculator.vb в Редакторе кода.

  2. Найдите каждый экземпляр следующего кода:

    varTotalCalculations += 1
    varTotalAsOfNow = varTotalCalculations
    

    Должно присутствовать четыре экземпляра этого кода, по одному для каждого метода вычислений.

  3. Измените этот код следующим образом.

    SyncLock Me
       varTotalCalculations += 1
       varTotalAsOfNow = varTotalCalculations
    End SyncLock
    
  4. Сохраните результаты работы и проверьте их, как в предыдущем примере.

    Можно заметить небольшое изменение в быстродействии программы. Это связано с тем, что выполнение потоков прекращается, когда в компоненте устанавливается монопольная блокировка. Несмотря на то что этот подход обеспечивает точность, он препятствует использованию некоторых преимуществ многопоточной обработки. Нужно осторожно относиться к блокировке потоков и применять ее только в случае крайней необходимости.

См. также

Задачи

Практическое руководство. Координирование нескольких потоков выполнения

Пошаговое руководство. Разработка простого многопоточного компонента с помощью Visual C#

Основные понятия

Обзор асинхронной модели, основанной на событиях

Ссылки

BackgroundWorker

Другие ресурсы

Программирование с использованием компонентов

Пошаговые руководства программирования компонентов

Многопоточность в компонентах