Главная
Трудоустройство
Ваши объявления
Наши новости
Информация







Пример использования многопоточности в приложениях

Рассмотрим довольно часто встречающуюся ситуацию: однопользовательское приложение, работающее с базой данных. В программе объявлены и используются объекты DataSet и DataTable. В результате действий пользователя состояние этих объектов изменяется: записи добавляются, удаляются, изменяется содержимое полей. Предлагаемый пример позволяет без лишних усилий реализовать внесение сделанных изменений в базу данных.







Исходная идея: вынести физическое взаимодействие с БД в отдельный фоновый поток. А в основном потоке работаем только с DataSet и DataTable. Метод, работающий в фоне, в бесконечном цикле периодически проверяет состояние всех таблиц в DataSetе и если есть изменения – отражает их в БД, после чего «засыпает» на некоторое время.

Что потребуется для работы:
- DataSet – собственно в нем данные
- OleDbDataAdapter – «мостик» между приложением и БД
- OleDbCommandBuilder – генерирует соответствующие SQL-команды для передачи в БД

Дабы не загромождать код примера будем считать, что переменные этих типов уже объявлены в программе и доступны нашему классу:

Friend DBConnection As New OleDbConnection()
Friend DBds As DataSet
Friend DBAdapter As OleDbDataAdapter
Friend DBCommandBuilder As New OleDbCommandBuilder(DBAdapter)
Внутренние переменные класса и код для установки этих свойств:
Imports System.Threading
Imports System.Data.OleDb
Public Class DataUpdater
	Private m_sleeper As Integer время на которое засыпает поток, в миллисекундах
	Private m_continue As Boolean переменная для принудительного завершения цикла
Private m_paused As Boolean переменная для временной приостановки цикла
Private aTable As DataTable переменная для перебора таблиц
	Private TempTable As DataTable таблица для временного хранения данных
Event TableUpdate(ByVal aTableName As String) событие генерируется после успешного 
 обновления данных из одной таблицы
 Property Paused() As Boolean
 Get
 Return m_paused
 End Get
 Set(ByVal Value As Boolean)
 m_paused = Value
 End Set
 End Property
 Property Continue() As Boolean
 Get
 Return m_continue
 End Get
 Set(ByVal Value As Boolean)
 m_continue = Value
 End Set
 End Property
 Property Sleeper() As Integer
 Get
 Return m_sleeper
 End Get
 Set(ByVal Value As Integer)
 m_sleeper = Value
 End Set
 End Property
End Class
Теперь собственно сам метод:
Friend Sub DoWork()
 Do While m_continue
 If Not m_paused Then
 For Each aTable In DBds.Tables
 TempTable = aTable.GetChanges()
 If Not TempTable Is Nothing Then UpdateOneTable(aTable)
 Next
 End If
 Thread.Sleep(m_sleeper)
 Loop
End Sub
Тут все достаточно просто: бесконечное повторение шагов
  • перебор всех таблиц: For Each aTable In DBds.Tables
  • проверка изменений в каждой таблице: TempTable = aTable.GetChanges(), метод GetChanges возвращает объект DataTable, содержащий измененные записи.
  • если есть изменения (If Not TempTable Is Nothing) - вызов процедуры UpdateOneTable для проверяемой таблицы
  • засыпание на m_sleeper миллисекунд: Thread.Sleep(m_sleeper)
  • Если переменная m_paused установлена в True, т.е. обновление БД временно приостановлено, то проверка таблиц не производится. При установке в True переменной m_continue происходит выход из цикла и поток завершается.

    Теперь о процедуре UpdateOneTable. Вот ее код:

    Friend Sub UpdateOneTable(ByRef onetable As DataTable)
     AddHandler DBAdapter.RowUpdated, New OleDbRowUpdatedEventHandler(AddressOf OnRowUpdated)
     Try
     DBAdapter.SelectCommand.CommandType = CommandType.TableDirect
     DBAdapter.SelectCommand.CommandText = onetable.TableName
     DBCommandBuilder.RefreshSchema()
     DBAdapter.InsertCommand = DBCommandBuilder.GetInsertCommand
     DBAdapter.UpdateCommand = DBCommandBuilder.GetUpdateCommand
     DBAdapter.DeleteCommand = DBCommandBuilder.GetDeleteCommand
     DBAdapter.Update(onetable)
     onetable.AcceptChanges()
     RaiseEvent TableUpdate(onetable.TableName)
     Catch ex As Exception
     MsgBox("При сохранении данных в таблице " & _
     onetable.TableName & " возникла следующая ошибка:" & vbCrLf & vbCrLf & _
     ex.Message & vbCrLf & vbCrLf, MsgBoxStyle.Critical)
     onetable.RejectChanges()
     End Try
     RemoveHandler DBAdapter.RowUpdated, New OleDbRowUpdatedEventHandler(AddressOf OnRowUpdated)
    End Sub
    
    Внутри блока Try ... End Try выполняются следующие действия:
  • в первых двух строках устанавливается текущая таблица объекта DBAdapter
  • DBCommandBuilder обновляет информацию о схеме данных вызовом метода RefreshSchema. После обновления схемы DBCommandBuilder может сгенерировать синтаксически правильные команды SQL для обновления, добавления и удаления записей в БД.
  • после присвоения соответствующих значений свойствам InsertCommand, UpdateCommand и DeleteCommand объекта DBAdapter можно вызвать его метод Update, который внесет изменения в таблицу, указанную в качестве параметра вызова.
  • после успешного обновления таблицы вызывается метод AcceptChanges объекта DataTable, который устанавливает свойство DataRowState измененных и добавленных записей в DataTable соответственно из Modified и Added в UnChanged, удаленные (Deleted) записи удаляются из DataTable.
  • генерируется событие TableUpdate для использования в приложении. Если на любом из этих шагов происходит ошибка и появляется исключение, то управление передается в блок Catch ... В этом блоке пользователь информируется о возникшей ошибке и о невозможности сохранить сделанные изменения, после чего методом RejectChanges откатываются назад сделанные в таблице изменения.
  • Теперь о ситуации, когда в обновляемой таблице есть поле типа счетчик и после обновления значение счетчика необходимо для дальнейших манипуляций с данными. Для получения значения счетчика служит следующая процедура (взято как есть из MSDN):
    Private Shared Sub OnRowUpdated(ByVal sender As Object, ByVal args As OleDbRowUpdatedEventArgs)
     Include a variable and a command to retrieve the identity value from the Access database.
     Dim newID As Integer = 0
     Dim idCMD As OleDbCommand = New OleDbCommand("SELECT @@IDENTITY", DBConnection)
     If args.StatementType = StatementType.Insert Then
     Retrieve the identity value and store it in the CategoryID column.
     newID = CInt(idCMD.ExecuteScalar())
     args.Row("s_thisID") = newID
     MsgBox("New ID is " & newID)
     End If
    End Sub
    
    Эта процедура подключается к событию RowUpdated объекта DBAdapter перед блоком Try. Внутри процедуры выполняются следующие действия:#
  • создается объект OLEDBCommand, выполняющий запрос к БД "SELECT @@IDENTITY". Этот запрос возвращает последнее значение типа счетчик, которое было использовано в БД.
  • Если запись была добавлена: If args.StatementType = StatementType.Insert (остальные случаи нас не интересуют), то выполняется запрос к БД
  • Полученное значение присваивается соответствующему полю записи: args.Row("s_thisID") = newID. В конкретном случае следует вместо "s_thisID" поставить имя конкретного поля.
  • Теперь об использовании класса.

    Создаем экземпляр класса:

    Friend WithEvents anUpdater As New DataUpdater()
    
    Устанавливаем свойства и запускаем поток:
    With anUpdater
     .Continue = True
     .Paused = False
     .Sleeper = 250 выполняем проверку 4 раза в секунду
    End With
    Dim thr As New Threading.Thread(AddressOf anUpdater.DoWork)
    With thr
     .IsBackground = True поток завершится сам при завершении приложения
     .Name = "Updaterthread"
     .Start()
    End With
    
    Собственно и все. Теперь все изменения в Datatableах будут вноситься в БД без нашего участия. Архив с кодом класса DataUpdater можно найти здесь.


    Этот материал не может быть воспроизведен без письменного разрешения владельцев авторских прав.



    www.cybersecurity.ru
    Что необходимо знать программисту 29-04-2007
    Использование технологии InternetExpress в Borland Delphi 20-08-2008 Что необходимо знать программисту
    Одной из примечательных возможностей Borland Delphi 5 является технология InternetExpress - средство обработки и публикации данных в Internet на основе технологии MIDAS.В Delphi реализован набор компонентов, позволяющих реализовать полный цикл клиент-серверной обработки данных на базе Internet как с использованием средств создания приложений на основе ISAPI/NSAPI, ASP и CGI, так и новых технологий, к примеру, стандарта XML (eXtended Markup La...


    Программный поиск файлов 07-07-2008 Что необходимо знать программисту
    В этом уроке мы с вами ознакомимся с основными принципами программной организации поиска файлов. Для начала определимся, зачем нам это может быть нужно. Например, вам нужно при запуске программы на выполнение просканировать определенный каталог на присутствие DOC файлов, и при наличии таковых открыть их на редактирование или напечатать. А как вам такая идея: фоновый поиск EXE файла в сети, и при обнаружении новой версии, автоматическое обновление...


    Деньги в Интернет 07-05-2008 Что необходимо знать программисту
    Интернет прочно входит в нашу жизнь. А вмести с ним такие понятие как электронная коммерция и электронные деньги. Многие предприниматели уже воспринимают пользователей Интернета как «потребителей» их интернет-рекламы, но и как потенциальных покупателей. Рассмотрим такие интересующие многих пользователей Интернета вопросы. 1) Что такое электронные деньги?2) Как их обналичивают (конвертируют).3) Как этот вопрос рег...

    Copyright © 2005
    "service-centers.com.ua"


    Rambler's Top100   Рейтинг@Mail.ru

    Работа
    Карта сайта
    О сайте
    Реклама
    все объявления
    поиск резюме
    поиск вакансий
    добавить резюме
    добавить вакансию

    Все объявления
    Компьютеры, оргтехника
    Автомобили
    Продукты питания
    Аудио, Фото, Видео
    Связь
    Отдых, путешествия
    Мебель
    Разное
    Добавить объявление
     Автомобили
     Ремонтно-строительные услуги
     Пресс-релизы
     Железо
     Soft
     Мобильная планета
     Для милых дам
     Родительский клуб
     Недвижимость
     Домашний очаг, усадьба
     Строительство
     Обустройство и безопасность дома
     Сантехника