Как выполнить метод в отдельном потоке?

Язык Visual Basic на платформе .NET.

Модераторы: Ramzes, Sebas

Thomas
Бывалый
Бывалый
Аватара пользователя
 
Сообщения: 246
Зарегистрирован: 12.11.2005 (Сб) 0:17
Откуда: "Сказочное королевство"

Как выполнить метод в отдельном потоке?

Сообщение Thomas » 12.08.2007 (Вс) 18:18

Имеется программа, котрая считывает данные(лист адресов клиентов, 30-40 тысяч) из excel файла, помещает их в таблицу ДатаСет, динамически создает новую пустую таблицу в Базе Данных соответствуюшей структуры. Далее из БД в ДС вызывается эта пустая таблица, заполняется данными и после этого нужно обновит таблицу в БД. Обновление происходит, но очень долго. При этом главная и дочерняя формы программы "зависают". Поэтому надо вывести обновление таблицы в БД в отдельный поток.
Раз поток должен работать автономно и не зависеть от главного потока, то я сделал следующее:
- написал компонент, единственной задачей которого является обновление таблицы.
- при создании экземпляра компонента, ему передаются в качестве параметров заполненная таблица в ДатаСет, sql- команда и колекция экземпляров класса, определяющего составные части адреса.
Код компонента
Код: Выделить всё

Public Class Component1
    Inherits System.ComponentModel.Component

    Public myThread As System.Threading.Thread

    Public m_count As Double
    Public m_sql As String
    Public m_dt As DataTable
    Public m_adComp As clsAddressComponent
    Public m_comps As Collection
    Private m_db As clsAccessToDB

    Public Event WorkComplete(ByVal count As Double)

    Public Sub New(ByVal comps As Collection, ByVal dt As DataTable, ByVal sql As String)
        m_sql = sql
        m_dt = dt
        m_comps = comps
    End Sub

    Public Sub FillTableInDataBase()
        m_db = New clsAccessToDB(My.Settings.DataBase)
        m_count = 0

        For Each dr As DataRow In m_dt.Rows
            SyncLock m_dt
                m_db.AddSqlParameter("RowId", dr("RowId"))
                For Each adComp In m_comps
                    m_db.AddSqlParameter(adComp.NamePost, dr(adComp.NamePost))
                Next
            End SyncLock
            m_db.DoNonQuery(m_sql)
            m_db.ClearSqlParametrs()
            m_count += 1
        Next

        RaiseEvent WorkComplete(m_count)
    End Sub

    Public Sub StartThread()
        myThread = New System.Threading.Thread(AddressOf FillTableInDataBase)
        myThread.Start()
    End Sub
End Class


А так он создается и вызывается его метод
Код: Выделить всё

Private Sub btnSchrijfXML_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles btnSchrijfXML.Click

        tableName = txtTableName.Text
        FillAddressComponents() 'создание колекции экз. классов описывающих составные части адреса
        CreateTable(tableName) ' создание татлицы нужной структуры в БД
        FillTableDataSet(tableName) ' вызов этой таблицы в ДатаСет и заполнение её значениями
        Dim sql As String = MakeSql() ' динамическое создание sql команды INSERT INTO

        component1 = New Component1(comps, dtOut, sql)
        component1.StartThread()
        AddHandler component1.WorkComplete, AddressOf TableIsFill

        FillPsCode(tableName)
        ShowMailingTable(tableName)
        WriteXml()
    End Sub

Если в компоненте не использовать Thread то он работает на Ура. :)
А если использовать Thread, то после обновления первой строки в таблице БД выскакивает исключение: http://img171.*** imageshack.us — идиотская служба, не пользуйтесь ею ***/img171/6303/foutif3.jpg

И я не понимаю почему студии не нравиться что, поменялись параметры sql-команды? :oops:

Каким образом можно заставить выполняться обновление таблицы в БД в отдельном потоке, что бы на это время не "зависала" вся программа.

Или подскажите где можно почитать нормально изложенный материал о том, как работает принцип многопоточности. Ведб все серьезные программы работают многопоточно, без Application.DoEvents().
И при этом потоки используют параметры.

ЗЫ В msdn смотрел материал изложен куцо и примитивно. Если задача чуть посложнее, на основании того, что они там пишут ничего не решить.
Met vriendelijke groetjes
VS2008 Pro FW3.5 SP1

asharky
Продвинутый пользователь
Продвинутый пользователь
Аватара пользователя
 
Сообщения: 162
Зарегистрирован: 22.06.2004 (Вт) 0:39
Откуда: Батоны-ларьки-поребрики...

Re: Как выполнить метод в отдельном потоке?

Сообщение asharky » 13.08.2007 (Пн) 13:17

Thomas писал(а):ЗЫ В msdn смотрел материал изложен куцо и примитивно. Если задача чуть посложнее, на основании того, что они там пишут ничего не решить.
Тут смотрел: http://msdn.microsoft.com/library/rus/d ... eading.asp :?:

Не плохо, "на пальцах", изложено про потоки в книге "Самоучитель Visual Basic 2005" издательства БХВ 2006года. Авторы: Д.Шевякова, А.Степанов, А.Карпов. Стр. 451...468.

Не найдёшь, могу отсканить. Но лучше бы мне этим не заниматься всё-таки, плз.
В культурной столице проститутки берут книгами...

Sebas
Неуловимый Джо
Неуловимый Джо
Аватара пользователя
 
Сообщения: 3626
Зарегистрирован: 12.02.2002 (Вт) 17:25
Откуда: столько наглости такие вопросы задавать

Сообщение Sebas » 13.08.2007 (Пн) 14:18

Тут что?

FillPsCode(tableName)
ShowMailingTable(tableName)
WriteXml()
- Я никогда не понимал, почему они приходят ко мне чтобы умирать?

sebas<-@->mail.ru

Thomas
Бывалый
Бывалый
Аватара пользователя
 
Сообщения: 246
Зарегистрирован: 12.11.2005 (Сб) 0:17
Откуда: "Сказочное королевство"

Сообщение Thomas » 13.08.2007 (Пн) 21:23

Sebas
Привествую.
FillPsCode(tableName) - метод работает с заполненной таблицей в БД. На основании другой таблицы модержащей коды предварительной сортировки, обновляется одна колонка во вновь созданной таблице.

ShowMailingTable(tableName) - метод создает в ДатаСет таблицу на основе готовой таблицы в БД. И передает её в ДатаГридВью для просмотра. В финальном варианте пользователю не будет показываться готовая таблица.
WriteXml() пишеться файл xml, данный беруться из таблицы в ДатаСет, которую создал предыдущий метод.

asharky
Приветствую.
Спасибо за "наводку" на книгу. Буду искать в сети, может кто уже отсканил и выложил в pdf.
А msdn там и смотрел, плюс msdn2.

Кстати попробовал в разных местах кода ставить брейкПойнты и обнаружил что, это сообщение касается не параметров sql, как я думал, а относиться к самой таблице в ДатаСет. Она каким-то образом "обнуляется". В ней пропадают все строки. Таблица есть, а строк нет. И происходит это не только после обновления первой строки, но могут обновиться несколько строк.

Моих скромных познаний не хватает, что бы разобраться что к чему. :oops:
Met vriendelijke groetjes
VS2008 Pro FW3.5 SP1

Sebas
Неуловимый Джо
Неуловимый Джо
Аватара пользователя
 
Сообщения: 3626
Зарегистрирован: 12.02.2002 (Вт) 17:25
Откуда: столько наглости такие вопросы задавать

Сообщение Sebas » 14.08.2007 (Вт) 9:14

Thomas

Код: Выделить всё
        For Each dr As DataRow In m_dt.Rows
            SyncLock m_dt
                m_db.AddSqlParameter("RowId", dr("RowId"))
                For Each adComp In m_comps
                    m_db.AddSqlParameter(adComp.NamePost, dr(adComp.NamePost))
                Next
            End SyncLock
            m_db.DoNonQuery(m_sql)
            m_db.ClearSqlParametrs()
            m_count += 1
        Next



поменяй на

Код: Выделить всё
            SyncLock m_dt
        For Each dr As DataRow In m_dt.Rows

                m_db.AddSqlParameter("RowId", dr("RowId"))
                For Each adComp In m_comps
                    m_db.AddSqlParameter(adComp.NamePost, dr(adComp.NamePost))
                Next

            m_db.DoNonQuery(m_sql)
            m_db.ClearSqlParametrs()
            m_count += 1
        Next
            End SyncLock
- Я никогда не понимал, почему они приходят ко мне чтобы умирать?

sebas<-@->mail.ru

Thomas
Бывалый
Бывалый
Аватара пользователя
 
Сообщения: 246
Зарегистрирован: 12.11.2005 (Сб) 0:17
Откуда: "Сказочное королевство"

Сообщение Thomas » 14.08.2007 (Вт) 9:34

Sebas
приветствую.
еще вчера перед тем как брейкПойнты ставить
вставил этот оператор.
Результат тот же, строки из таблицы исчезают непонятным образом.
Причем это происходит "рандомно", то после первой строки, а то и после 80-й.
Если бы я эту таблицу передавал компоненту как ссылочный тип (ByRef) можно было бы подумать что, основной поток с ней что-то делает. А так я передаю её по значению (ByVal) и компонент получает её. И кроме как, считывать из нее данные, ничего больше с ней не делает.
Met vriendelijke groetjes
VS2008 Pro FW3.5 SP1

Sebas
Неуловимый Джо
Неуловимый Джо
Аватара пользователя
 
Сообщения: 3626
Зарегистрирован: 12.02.2002 (Вт) 17:25
Откуда: столько наглости такие вопросы задавать

Сообщение Sebas » 14.08.2007 (Вт) 11:25

synclock m_dt.Rows ?

Вот эти процедуры выложи
FillPsCode(tableName)
ShowMailingTable(tableName)
WriteXml()
- Я никогда не понимал, почему они приходят ко мне чтобы умирать?

sebas<-@->mail.ru

Thomas
Бывалый
Бывалый
Аватара пользователя
 
Сообщения: 246
Зарегистрирован: 12.11.2005 (Сб) 0:17
Откуда: "Сказочное королевство"

Сообщение Thomas » 15.08.2007 (Ср) 9:38

Sebas
Приветствую.
synclock m_dt.Rows ?
Да.
Код: Выделить всё

Private Sub FillPsCode(ByVal tableName As String)
        Dim sql As String = "UPDATE tblPreSortingCode INNER JOIN " + tableName
        sql += " ON tblPreSortingCode.POSTCODE = " + tableName + ".PostalCode"
        sql += " SET " + tableName + ".psCode = tblPreSortingCode.SORTPLAN;"
        db.DoNonQuery(sql)
    End Sub 'in orde
Private Sub ShowMailingTable(ByVal tableName As String)
        Dim Sql As String = "SELECT * FROM " + tableName
        db.FillDataSet(tableName, Sql, False)
        dgvOutputData.DataSource = Nothing
        dgvOutputData.DataSource = db.myDs
        dgvOutputData.DataMember = tableName
        dgvOutputData.Refresh()
    End Sub 'in orde
Private Sub WriteXml()
        Dim mode As String = cboMode.SelectedItem.ToString()
        Dim m As clsMailing = New clsMailing(comps, db.myDs, strFileName, tableName, mode)
        m.Create_XmlMailingCheck() 'код класса выложил отдельным файлом.
        'data base aanpassen
        m.InsertMailing()
        m.InsertFile()
        tslStatus.Text = "MailingCheck XML-bestand is geschrijven."
    End Sub 'in orde
' метод из класса db
''' <summary>
    ''' Метод для заполнения данными таблицы в ДатаСет
    ''' </summary>
    ''' <param name="tableName">Имя таблицы в ДатаСет, в которую будут добавлены данные.</param>
    ''' <param name="sql">SQL-команда определяющая данные которые будут добавлены.</param>
    ''' <param name="isnew">Указывает на новая или уже существующая таблица.</param>
    ''' <remarks>Если указанная таблица существует, она будет очищена от значений и снова заполнена. Если нет, то таблица создается и заполняется данными.</remarks>
    Public Sub FillDataSet(ByVal tableName As String, ByVal sql As String, ByVal isnew As Boolean)
        If isnew = False Then
            ds.Tables(tableName).Clear()
        End If
        Try
            cmd.CommandText = sql
            da.Fill(ds, tableName)
        Catch ex As Exception
            System.Windows.Forms.MessageBox.Show(ex.Message, "Fout bij uitvoering van aanvraag ann data base.")
        End Try
    End Sub
Вложения
clsMailing.rar
класс для создания xml.
(3.57 Кб) Скачиваний: 81
Met vriendelijke groetjes
VS2008 Pro FW3.5 SP1

asharky
Продвинутый пользователь
Продвинутый пользователь
Аватара пользователя
 
Сообщения: 162
Зарегистрирован: 22.06.2004 (Вт) 0:39
Откуда: Батоны-ларьки-поребрики...

Сообщение asharky » 16.08.2007 (Чт) 14:07

Thomas писал(а):...Она каким-то образом "обнуляется". В ней пропадают все строки. Таблица есть, а строк нет. И происходит это не только после обновления первой строки, но могут обновиться несколько строк.

Моих скромных познаний не хватает, что бы разобраться что к чему. :oops:
AcceptChanges ?

http://msdn.microsoft.com/library/rus/default.asp?url=/library/rus/cpref/html/frlrfsystemdatadatatableclassacceptchangestopic.asp
В культурной столице проститутки берут книгами...

Thomas
Бывалый
Бывалый
Аватара пользователя
 
Сообщения: 246
Зарегистрирован: 12.11.2005 (Сб) 0:17
Откуда: "Сказочное королевство"

Сообщение Thomas » 16.08.2007 (Чт) 15:10

asharky
Приветствую.
AcceptChanges это ни причем. Его там нет.
Код: Выделить всё
m_db.DoNonQuery(sql)

вызывает метод из класса
Код: Выделить всё

Public Sub DoNonQuery(ByVal sql As String)
        Try
            con.Open()
            cmd.CommandText = sql
            cmd.ExecuteNonQuery()
        Catch ex As Exception
            System.Windows.Forms.MessageBox.Show(ex.Message, "Ошибка при выполнении запроса к БД!")
        Finally
            con.Close()
        End Try
    End Sub


Таблица существует в ДатаСет. Да, она была создана на основе таблицы из БД, заполнена данными. И теперь эти данные должны быть построчно считаны и записаны в таблицу БД.
Т.е. при выполнении метода с таблицей ничего кроме чтения значений не происходит. Не добавляются и не удаляются строки, не изменяются значения в полях. Просто данные из нее должны быть переписаны в БД.
Я тут попробовал использовать BackgroundWorker
Код: Выделить всё

Private Sub BackgroundWorker1_DoWork(ByVal sender As System.Object, ByVal e As System.ComponentModel.DoWorkEventArgs) Handles BackgroundWorker1.DoWork
        Dim bgw As BackgroundWorker = CType(sender, BackgroundWorker)
        Dim sql As String = MakeSql()
        e.Result = FillTableDataBase(sql, dtOut, bgw, e)
    End Sub

    Private Sub BackgroundWorker1_ProgressChanged(ByVal sender As System.Object, ByVal e As System.ComponentModel.ProgressChangedEventArgs) Handles BackgroundWorker1.ProgressChanged
        ToolStripProgressBar1.Value = e.ProgressPercentage
    End Sub

    Private Sub BackgroundWorker1_RunWorkerCompleted(ByVal sender As System.Object, ByVal e As System.ComponentModel.RunWorkerCompletedEventArgs) Handles BackgroundWorker1.RunWorkerCompleted
        If e.Cancelled Then
            tslStatus.Text = "Processing aborted"
        Else
            tslStatus.Text = "Processing completed: "
            tslStatus.Text &= Convert.ToInt32(e.Result).ToString
        End If
    End Sub
Private Function FillTableDataBase(ByVal sql As String, ByVal dt As DataTable, ByVal bgworker As BackgroundWorker, ByVal e As DoWorkEventArgs) As Integer
        Dim progress, loper As Integer  'БУДЕМ СЧИТАТЬ СТРОКИ, которые обновились.
        For Each dr As DataRow In dt.Rows
            db.AddSqlParameter("RowId", dr("RowId"))
            For Each adComp In comps
                db.AddSqlParameter(adComp.NamePost, dr(adComp.NamePost))
            Next
            db.DoNonQuery(sql)
            db.ClearSqlParametrs()
            loper += 1
            progress = (loper * 100) / aantal
            bgworker.ReportProgress(progress)
            If bgworker.CancellationPending Then
                e.Cancel = True
                Exit For
            End If
        Next
        Return progress
    End Function 'in orde
Private Sub btnSchrijfXML_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles btnSchrijfXML.Click

        tableName = txtTableName.Text

        If GetPermission() Then
            FillAddressComponents()
            CreateTable(tableName)
            FillTableDataSet(tableName)
            BackgroundWorker1.RunWorkerAsync()
            FillPsCode(tableName)
            ShowMailingTable(tableName)
            WriteXml()
        End If
    End Sub

Так происходит тоже самое
Строки из dt.Rows загадочным образом исчезают. И соответственно for не выполняется.
А вот почему и куда они исчезают, я не нашел. :(
Met vriendelijke groetjes
VS2008 Pro FW3.5 SP1

Thomas
Бывалый
Бывалый
Аватара пользователя
 
Сообщения: 246
Зарегистрирован: 12.11.2005 (Сб) 0:17
Откуда: "Сказочное королевство"

Сообщение Thomas » 18.08.2007 (Сб) 0:35

Приветствую всех.
Куда девались строки, я нашел.
Их убивал основной поток, который не ждал когда новый поток выполнит свою работу, а делал свою.
Thomas писал(а):ShowMailingTable(tableName) - метод создает в ДатаСет таблицу на основе готовой таблицы в БД. И передает её в ДатаГридВью для просмотра.

Вот тут были грабли. Этот метод удалял "старую" таблицу в ДатаСет и вместо нее писал "новую". Ну а она соответственно была пустой.

Sebas
asharky
Спасибо за внимание и попытку помочь разобраться.
Ну и за "пинки" в нужном направлении. :D
Met vriendelijke groetjes
VS2008 Pro FW3.5 SP1

asharky
Продвинутый пользователь
Продвинутый пользователь
Аватара пользователя
 
Сообщения: 162
Зарегистрирован: 22.06.2004 (Вт) 0:39
Откуда: Батоны-ларьки-поребрики...

Сообщение asharky » 18.08.2007 (Сб) 14:57

Thomas писал(а):asharky
Спасибо за внимание и попытку помочь разобраться.
Ну и за "пинки" в нужном направлении. :D
Нашлись сканы о которых я выше говорил.

PS. Извиняюсь, что побил на части, но тут ограничения на размер файла.
Вложения
p.part1.rar
Потоки в .NET
(1.39 МиБ) Скачиваний: 87
p.part2.rar
(1.39 МиБ) Скачиваний: 83
p.part3.rar
(1.39 МиБ) Скачиваний: 74
p.part4.rar
(1.39 МиБ) Скачиваний: 80
p.part5.rar
(712.67 Кб) Скачиваний: 81
В культурной столице проститутки берут книгами...

Thomas
Бывалый
Бывалый
Аватара пользователя
 
Сообщения: 246
Зарегистрирован: 12.11.2005 (Сб) 0:17
Откуда: "Сказочное королевство"

Сообщение Thomas » 18.08.2007 (Сб) 15:03

asharky
Спасибо.
Самое прикольное, что у меня есть вся кника в djvu формате.
Могу заслать на мыло.

Сейчас закончу дрова колоть. И снова засяду книги читать и применять полученные знания на практике. :D
Met vriendelijke groetjes
VS2008 Pro FW3.5 SP1

asharky
Продвинутый пользователь
Продвинутый пользователь
Аватара пользователя
 
Сообщения: 162
Зарегистрирован: 22.06.2004 (Вт) 0:39
Откуда: Батоны-ларьки-поребрики...

Сообщение asharky » 18.08.2007 (Сб) 15:57

Thomas писал(а):asharky
Спасибо.
Самое прикольное, что у меня есть вся кника в djvu формате.
Могу заслать на мыло.
Зашли пожалуйста на asharky@opensea.ru
В культурной столице проститутки берут книгами...


Вернуться в Visual Basic .NET

Кто сейчас на конференции

Сейчас этот форум просматривают: нет зарегистрированных пользователей и гости: 64

    TopList