|
Пример использования многопоточности в приложениях
Рассмотрим довольно часто встречающуюся ситуацию:
однопользовательское приложение, работающее с базой данных. В
программе объявлены и используются объекты 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) Как этот вопрос рег... |