Форма с тремя браузерами в отдельных потоках

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

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

euroflock
Обычный пользователь
Обычный пользователь
 
Сообщения: 68
Зарегистрирован: 26.02.2009 (Чт) 12:54

Форма с тремя браузерами в отдельных потоках

Сообщение euroflock » 02.04.2009 (Чт) 19:52

Помогите разобраться с мультипоточностью на понятном примере (актуальном для меня).
Есть форма (Form1).
На форме есть:
  • мультистрочное текстовое поле (TextBox1)
  • две кнопки (Button1 и Button2)
  • три вкладки в элементе TabControl1 (TabPage1, TabPage3, TabPage3)
  • три браузера, по одному в каждой вкладке (WebBrowser1, WebBrowser2, WebBrowser3)

Создаём три потока, причём Поток1 работает только с Браузером1 (и т.д.)
Всё это можно быстро произвольно сделать несколькими щелчками мышки...
Каждый поток загружает в "своём" браузере страничку сайта (все адреса в массиве), подсчитывает количество ссылок на странице и выводит количество найденных ссылок в одно текстовое поле на форме (во всяком случае хотелось бы, чтобы всё так и происходило).

Код: Выделить всё
Imports System
Imports System.Windows.Forms
Imports System.Threading

Public Class Form1

    Dim thr1 As Thread
    Dim thr2 As Thread
    Dim thr3 As Thread
    Public InputUrls() As String = {"http://vbstreets.ru/", "http://bbs.vbstreets.ru/", "http://bbs.vbstreets.ru/viewforum.php?f=2"}

    Private Sub Form1_Load(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles MyBase.Load
        Me.Button1.Text = "Start"
        Me.Button2.Text = "Stop"
    End Sub

    Private Sub Button1_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Button1.Click
        Me.Button1.Enabled = False
        Dim thr1 As New Thread(AddressOf WorkThread1)
        Dim thr2 As New Thread(AddressOf WorkThread2)
        Dim thr3 As New Thread(AddressOf WorkThread3)
        thr1.Start()
        thr2.Start()
        thr3.Start()
    End Sub

    Private Sub Button2_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Button2.Click
        ' thr1.Abort() - убираем в связи с некрасивостью
        ' thr2.Abort() - убираем в связи с некрасивостью
        ' thr3.Abort() - убираем в связи с некрасивостью
        Me.Button1.Enabled = True
    End Sub

    Sub WorkThread1()
        Dim address As String = InputUrls(0) '"http://vbstreets.ru/"
        WebBrowser1.Navigate(address)
        While WebBrowser1.IsBusy : End While 'wait complete navigating
        If WebBrowser1.Document IsNot Nothing Then
            Dim WbDoc1 As HtmlDocument = WebBrowser1.Document
            Dim links1 As HtmlElementCollection = WbDoc1.GetElementsByTagName("A")
            Dim CountLinks1 As Integer = links1.Count
            Dim res As String = vbCrLf & "URL 1 have: " & CountLinks1 & " links"
            Me.TextBox1.Text &= res
        Else : Exit Sub : End If
    End Sub

    Sub WorkThread2()
        Dim address As String = InputUrls(1) '"http://bbs.vbstreets.ru/"
        WebBrowser2.Navigate(address)
        While WebBrowser2.IsBusy : End While 'wait complete navigating
        If WebBrowser2.Document IsNot Nothing Then
            Dim WbDoc2 As HtmlDocument = WebBrowser2.Document
            Dim links2 As HtmlElementCollection = WbDoc2.GetElementsByTagName("A")
            Dim CountLinks2 As Integer = links2.Count
            Dim res As String = vbCrLf & "URL 2 have: " & CountLinks2 & " links"
            Me.TextBox1.Text &= res
        Else : Exit Sub : End If
    End Sub

    Sub WorkThread3()
        Dim address As String = InputUrls(2) '"http://bbs.vbstreets.ru/viewforum.php?f=2"
        WebBrowser3.Navigate(address)
        While WebBrowser3.IsBusy : End While 'wait complete navigating
        If WebBrowser3.Document IsNot Nothing Then
            Dim WbDoc3 As HtmlDocument = WebBrowser3.Document
            Dim links3 As HtmlElementCollection = WbDoc3.GetElementsByTagName("A")
            Dim CountLinks3 As Integer = links3.Count
            Me.TextBox1.Text &= vbCrLf & "URL 3 have: " & CountLinks3 & " links"
        Else : Exit Sub : End If
    End Sub

End Class


1. Как переделать этот код, чтобы заработало?
2. Каким образом передать параметры в процедуру, на которую указывает AddressOf в строках
Код: Выделить всё
Dim thr1 As New Thread(AddressOf WorkThread1)
Dim thr2 As New Thread(AddressOf WorkThread2)
Dim thr3 As New Thread(AddressOf WorkThread3)
таким образом, чтобы не создавать три разные процедуры WorkThread1, WorkThread2 и WorkThread3, а всего одну WorkThread(NumberThread) с параметром номера потока NumberThread?
Последний раз редактировалось euroflock 02.04.2009 (Чт) 20:11, всего редактировалось 2 раз(а).

Williams
Гуру
Гуру
Аватара пользователя
 
Сообщения: 1280
Зарегистрирован: 06.05.2008 (Вт) 18:35
Откуда: System.Reflection.Williams (увидел себя в зеркале :))

Re: Форма с тремя браузерами в отдельных потоках

Сообщение Williams » 02.04.2009 (Чт) 20:00

Пока не вдавался в подробности, но .Abort выглядит некрасиво, если есть возможность обойтись без этого, стоит использовать флаги остановки. Второе, есть возможность передать потоку параметры, но в BackgroundWorker компоненте это сделать гораздо проще (RunWorkerAsync имеет вариант с параметром)
И вы думаете, что вас оставят в живых после прочтения этого поста?

euroflock
Обычный пользователь
Обычный пользователь
 
Сообщения: 68
Зарегистрирован: 26.02.2009 (Чт) 12:54

Re: Форма с тремя браузерами в отдельных потоках

Сообщение euroflock » 02.04.2009 (Чт) 20:09

Williams писал(а):Пока не вдавался в подробности, но .Abort выглядит некрасиво, если есть возможность обойтись без этого, стоит использовать флаги остановки. Второе, есть возможность передать потоку параметры, но в BackgroundWorker компоненте это сделать гораздо проще (RunWorkerAsync имеет вариант с параметром)


По-моему здесь пока некрасиво почти всё т.к. ничего не работает. А Вам не трудно было бы показать на примерчике, как реализовать поставленную задачу по схеме "гораздо проще" (с применением BackgroundWorker)? Для этого нужен один BackgroundWorker1 или несколько (по одному на каждый процесс)?

Nord777
Гуру
Гуру
Аватара пользователя
 
Сообщения: 1144
Зарегистрирован: 22.02.2004 (Вс) 13:15
Откуда: Подольск

Re: Форма с тремя браузерами в отдельных потоках

Сообщение Nord777 » 03.04.2009 (Пт) 2:19

euroflock
Неудачный пример. Этот компонент не работает в многопоточной среде.
Microsoft Visual Studio 2008
Microsoft .NET Framework 3.5

euroflock
Обычный пользователь
Обычный пользователь
 
Сообщения: 68
Зарегистрирован: 26.02.2009 (Чт) 12:54

Re: Форма с тремя браузерами в отдельных потоках

Сообщение euroflock » 03.04.2009 (Пт) 7:48

Nord777 писал(а):euroflock
Неудачный пример.


Пример из серии - каким бы хотелось его видеть, если бы всё так и работало (здесь я привожу ход своих мыслей, приводящих меня в тупик)

Nord777 писал(а):euroflock
Этот компонент не работает в многопоточной среде.


А какой именно этот компонент?
Если какой-нибудь компонент не работатет в многопоточной среде, то у него по идее не должно быть методов .BeginInvoke, .EndInvoke и наконец .InvokeRequired.

Если Вы сделаете подобную форму у себя в новом проекте, то увидите, что после нажатия на кнопку Start все страницы загружаются (хотя и не без ошибок). Так, если перед стартом сделать активной вкладку TabPage2, то ошибка выскакивает только при загрузке страницы в WebBrowser3 на TabPage3, а если сделать активной вкладку TabPage3 (перед стартом), то ошибка выскакивает только при загрузке страницы в WebBrowser2 на TabPage2. WebBrowser1 на TabPage1 ошибок при загрузке страницы не вызывает. Однако пока этим всё и заканчивается - ничего не подсчитывается и не выводится.

Williams
Гуру
Гуру
Аватара пользователя
 
Сообщения: 1280
Зарегистрирован: 06.05.2008 (Вт) 18:35
Откуда: System.Reflection.Williams (увидел себя в зеркале :))

Re: Форма с тремя браузерами в отдельных потоках

Сообщение Williams » 03.04.2009 (Пт) 10:37

Так-то да, могут быть проблемы, но если делать Invoke в нужном месте то все будет ок.
И вы думаете, что вас оставят в живых после прочтения этого поста?

Nord777
Гуру
Гуру
Аватара пользователя
 
Сообщения: 1144
Зарегистрирован: 22.02.2004 (Вс) 13:15
Откуда: Подольск

Re: Форма с тремя браузерами в отдельных потоках

Сообщение Nord777 » 03.04.2009 (Пт) 12:39

А какой именно этот компонент?
WebBrowser.


euroflock писал(а):Если какой-нибудь компонент не работатет в многопоточной среде, то у него по идее не должно быть методов .BeginInvoke, .EndInvoke
MSDN писал(а):Control.BeginInvoke - метод
Выполняет делегат асинхронно в потоке, в котором был создан базовый дескриптор элемента управления.




Код: Выделить всё
Public Class Form1
   Dim SB As New System.Text.StringBuilder()
   Private Delegate Sub УказательНаМетод_Navigate(ByVal Browser As WebBrowser, ByVal UrlString As String)

   Private Sub Form1_FormClosing(ByVal sender As Object, ByVal e As System.Windows.Forms.FormClosingEventArgs) Handles Me.FormClosing
      WebBrowser1.Dispose() : WebBrowser2.Dispose() : WebBrowser3.Dispose()
   End Sub

   Private Sub Form1_Load(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles MyBase.Load
      AddHandler WebBrowser1.DocumentCompleted, AddressOf DocumentCompleted
      AddHandler WebBrowser2.DocumentCompleted, AddressOf DocumentCompleted
      AddHandler WebBrowser3.DocumentCompleted, AddressOf DocumentCompleted
      Me.Button1.Text = "Start"
      Me.Button2.Text = "Stop"
   End Sub

   Private Sub Button1_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Button1.Click
      Dim NavigateProc As New УказательНаМетод_Navigate(AddressOf Navigate)
      WebBrowser1.BeginInvoke(NavigateProc, WebBrowser1, "http://URL1")
      WebBrowser2.BeginInvoke(NavigateProc, WebBrowser2, "http://URL2")
      WebBrowser3.BeginInvoke(NavigateProc, WebBrowser3, "http://URL3")
   End Sub

   Private Sub Navigate(ByVal Browser As WebBrowser, ByVal UrlString As String)
      Browser.Navigate(UrlString)
   End Sub

   Private Sub DocumentCompleted(ByVal sender As Object, ByVal e As System.Windows.Forms.WebBrowserDocumentCompletedEventArgs)
      Dim browser As WebBrowser = DirectCast(sender, WebBrowser)
      'If browser.Url <> e.Url Then Exit Sub
      SB.Length = 0
      SB.AppendLine(e.Url.ToString)
      SB.AppendLine("have: " & browser.Document.Links.Count & " links")
      SB.AppendLine("InvokeRequired - " & Me.InvokeRequired)
      Me.TextBox1.Text &= SB.ToString
   End Sub

End Class
Microsoft Visual Studio 2008
Microsoft .NET Framework 3.5

euroflock
Обычный пользователь
Обычный пользователь
 
Сообщения: 68
Зарегистрирован: 26.02.2009 (Чт) 12:54

Re: Форма с тремя браузерами в отдельных потоках

Сообщение euroflock » 03.04.2009 (Пт) 13:31

Nord777 писал(а):
WebBrowser


Конечно же Ваш код гораздо компактнее. Спасибо, всё работает!
Я также не сидел сложа руки. Хотя мой код гораздо длиннее (вероятно его можно как-то сократить), но также работает...
Код: Выделить всё
Imports System
Imports System.Windows.Forms
Imports System.Threading

Public Class Form1
    Delegate Sub Nav1Callback(ByVal [address] As String)
    Delegate Sub Nav2Callback(ByVal [address] As String)
    Delegate Sub Nav3Callback(ByVal [address] As String)
    Delegate Sub SetCountsCallback(ByVal [count] As String)
    Delegate Function wb1DocCallback() As HtmlDocument
    Delegate Function wb2DocCallback() As HtmlDocument
    Delegate Function wb3DocCallback() As HtmlDocument
    Delegate Function wb1DocTextCallback() As String
    Delegate Function wb2DocTextCallback() As String
    Delegate Function wb3DocTextCallback() As String

    Public InputUrls() As String = {"http://vbstreets.ru/", "http://www.microsoft.com/", "http://msdn.microsoft.com/"}

    Private Sub Form1_FormClosing(ByVal sender As Object, ByVal e As System.Windows.Forms.FormClosingEventArgs) Handles Me.FormClosing
        WebBrowser1.Dispose() : WebBrowser2.Dispose() : WebBrowser3.Dispose()
    End Sub

    Private Sub Form1_Load(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles MyBase.Load
        Me.Button1.Text = "Start"
        Me.Button2.Text = "Stop"
    End Sub

    Private Sub Button1_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Button1.Click
        Me.Button1.Enabled = False
        Dim thr1 As New Thread(AddressOf WorkThread1)
        Dim thr2 As New Thread(AddressOf WorkThread2)
        Dim thr3 As New Thread(AddressOf WorkThread3)
        thr1.Start()
        thr2.Start()
        thr3.Start()
    End Sub

    Private Sub Button2_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Button2.Click
        Me.TextBox1.Text = ""
        Me.Button1.Enabled = True
    End Sub

    Sub WorkThread1()
        Dim address As String = InputUrls(0) '"http://vbstreets.ru/"
        Nav1([address])
        While wb1Doc() Is Nothing Or Not wb1DocText().ToUpper.Contains("</HTML>")
            Thread.Sleep(100) 'wait for browser navigating complete
        End While
        Dim WbDoc As HtmlDocument = wb1Doc()
        Dim links As HtmlElementCollection = WbDoc.GetElementsByTagName("A")
        Dim CountLinks As Integer = links.Count
        Dim res As String = "Thread 1. URL " & address & " have: " & CountLinks & " links" & vbCrLf
        SetCounts(res)
    End Sub

    Sub WorkThread2()
        Dim address As String = InputUrls(1) '"http://www.microsoft.com/"
        Nav2([address])
        While wb2Doc() Is Nothing Or Not wb2DocText().ToUpper.Contains("</HTML>")
            Thread.Sleep(200) 'wait for browser navigating complete
        End While
        Dim WbDoc As HtmlDocument = wb2Doc()
        Dim links As HtmlElementCollection = WbDoc.GetElementsByTagName("A")
        Dim CountLinks As Integer = links.Count
        Dim res As String = "Thread 2. URL " & address & " have: " & CountLinks & " links" & vbCrLf
        SetCounts(res)
    End Sub

    Sub WorkThread3()
        Dim address As String = InputUrls(2) '"http://msdn.microsoft.com/"
        Nav3([address])
        While wb3Doc() Is Nothing Or Not wb3DocText().ToUpper.Contains("</HTML>")
            Thread.Sleep(300) 'wait for browser navigating complete
        End While
        Dim WbDoc As HtmlDocument = wb3Doc()
        Dim links As HtmlElementCollection = WbDoc.GetElementsByTagName("A")
        Dim CountLinks As Integer = links.Count
        Dim res As String = "Thread 3. URL " & address & " have: " & CountLinks & " links" & vbCrLf
        SetCounts(res)
    End Sub

    Private Sub SetCounts(ByVal [count] As String)
        If Me.TextBox1.InvokeRequired Then
            Dim d As New SetCountsCallback(AddressOf SetCounts)
            Me.Invoke(d, New Object() {[count]})
        Else
            Me.TextBox1.Text &= [count]
        End If
    End Sub

    Private Sub Nav1(ByVal [address] As String)
        If Me.WebBrowser1.InvokeRequired Then
            Dim d As New Nav1Callback(AddressOf Nav1)
            Me.Invoke(d, New Object() {[address]})
        Else
            Me.WebBrowser1.Navigate([address])
        End If
    End Sub

    Private Sub Nav2(ByVal [address] As String)
        If Me.WebBrowser2.InvokeRequired Then
            Dim d As New Nav2Callback(AddressOf Nav2)
            Me.Invoke(d, New Object() {[address]})
        Else
            Me.WebBrowser2.Navigate([address])
        End If
    End Sub

    Private Sub Nav3(ByVal [address] As String)
        If Me.WebBrowser3.InvokeRequired Then
            Dim d As New Nav3Callback(AddressOf Nav3)
            Me.Invoke(d, New Object() {[address]})
        Else
            Me.WebBrowser3.Navigate([address])
        End If
    End Sub

    Private Function wb1DocText() As String
        If Me.WebBrowser1.InvokeRequired Then
            Dim d As New wb1DocTextCallback(AddressOf wb1DocText)
            Return Me.Invoke(d, New Object() {})
        Else
            Return Me.WebBrowser1.DocumentText
        End If
    End Function

    Private Function wb2DocText() As String
        If Me.WebBrowser2.InvokeRequired Then
            Dim d As New wb2DocTextCallback(AddressOf wb2DocText)
            Return Me.Invoke(d, New Object() {})
        Else
            Return Me.WebBrowser2.DocumentText
        End If
    End Function

    Private Function wb3DocText() As String
        If Me.WebBrowser3.InvokeRequired Then
            Dim d As New wb3DocTextCallback(AddressOf wb3DocText)
            Return Me.Invoke(d, New Object() {})
        Else
            Return Me.WebBrowser3.DocumentText
        End If
    End Function

    Private Function wb1Doc() As HtmlDocument
        If Me.WebBrowser1.InvokeRequired Then
            Dim d As New wb1DocCallback(AddressOf wb1Doc)
            Return Me.Invoke(d, New Object() {})
        Else
            Return Me.WebBrowser1.Document
        End If
    End Function

    Private Function wb2Doc() As HtmlDocument
        If Me.WebBrowser2.InvokeRequired Then
            Dim d As New wb2DocCallback(AddressOf wb2Doc)
            Return Me.Invoke(d, New Object() {})
        Else
            Return Me.WebBrowser2.Document
        End If
    End Function

    Private Function wb3Doc() As HtmlDocument
        If Me.WebBrowser3.InvokeRequired Then
            Dim d As New wb3DocCallback(AddressOf wb3Doc)
            Return Me.Invoke(d, New Object() {})
        Else
            Return Me.WebBrowser3.Document
        End If
    End Function

И ещё, я обратил внимание, что в моём коде я всегда имею возможность получить важные данные WebBrowser.Document, WebBrowser.DocumentText и т.д., однако таким же образом получить доступ к самому объекту WebBrowser не получается.

Когда Ваш код полностью отработал (потоки закрылись, данные отобразились), каким образом я могу обратиться к WebBrowser.Document?

Nord777
Гуру
Гуру
Аватара пользователя
 
Сообщения: 1144
Зарегистрирован: 22.02.2004 (Вс) 13:15
Откуда: Подольск

Re: Форма с тремя браузерами в отдельных потоках

Сообщение Nord777 » 03.04.2009 (Пт) 13:59

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

код полностью отработал (потоки закрылись, данные отобразились),
Нет тут никаких других потоков. Весь код отрабатывает в основном потоке формы.

каким образом я могу обратиться к WebBrowser.Document?
Как обычно:
... = WebBrowser1.Document
... = WebBrowser2.Document
... = WebBrowser3.Document
Microsoft Visual Studio 2008
Microsoft .NET Framework 3.5

euroflock
Обычный пользователь
Обычный пользователь
 
Сообщения: 68
Зарегистрирован: 26.02.2009 (Чт) 12:54

Re: Форма с тремя браузерами в отдельных потоках

Сообщение euroflock » 03.04.2009 (Пт) 16:00

"Сжал" свой последний вариант и вот что получилось (относительно поставленной задачи - вызов браузеров в отдельных потоках):
Код: Выделить всё
Imports System
Imports System.Windows.Forms
Imports System.Threading

Public Class Form1
    Delegate Sub NavCb(ByVal [number] As Integer, ByVal [address] As String)
    Delegate Function wbDocCb(ByVal [number] As Integer) As HtmlDocument
    Delegate Function wbDocTextCb(ByVal [number] As Integer) As String
    Delegate Sub SetCountsCallback(ByVal [count] As String)
    Public InputUrls() As String = {"http://vbstreets.ru/", "http://www.microsoft.com/", "http://msdn.microsoft.com/"}

    Private Sub Form1_FormClosing(ByVal sender As Object, ByVal e As System.Windows.Forms.FormClosingEventArgs) Handles Me.FormClosing
        WebBrowser1.Dispose() : WebBrowser2.Dispose() : WebBrowser3.Dispose()
    End Sub

    Private Sub Form1_Load(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles MyBase.Load
        Me.Button1.Text = "Start"
        Me.Button2.Text = "Stop"
    End Sub

    Private Sub Button1_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Button1.Click
        Me.Button1.Enabled = False
        Dim thr1 As New Thread(AddressOf WorkThread)
        Dim thr2 As New Thread(AddressOf WorkThread)
        Dim thr3 As New Thread(AddressOf WorkThread)
        thr1.Start(1) : thr2.Start(2) : thr3.Start(3)
    End Sub

    Private Sub Button2_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Button2.Click
        Me.TextBox1.Text = ""
        Me.WebBrowser1.DocumentText = ""
        Me.WebBrowser2.DocumentText = ""
        Me.WebBrowser3.DocumentText = ""
        Me.Button1.Enabled = True
    End Sub

    Sub WorkThread(ByVal [browser] As Integer)
        Dim address As String = InputUrls([browser] - 1)
        Nav([browser], [address])
        While wbDoc([browser]) Is Nothing Or Not wbDocText([browser]).ToUpper.Contains("</HTML>")
            Thread.Sleep(100) 'wait for browser navigating complete
        End While
        Dim WbDocum As HtmlDocument = wbDoc([browser])
        Dim links As HtmlElementCollection = WbDocum.GetElementsByTagName("A")
        Dim CountLinks As Integer = links.Count
        Dim res As String = "Thread " & [browser] & ". URL " & address & " have: " & CountLinks & " links" & vbCrLf
        SetCounts(res)
    End Sub

    Private Sub SetCounts(ByVal [count] As String)
        If Me.TextBox1.InvokeRequired Then
            Dim d As New SetCountsCallback(AddressOf SetCounts)
            Me.Invoke(d, New Object() {[count]})
        Else
            Me.TextBox1.Text &= [count]
        End If
    End Sub

    Private Sub Nav(ByVal [number] As Integer, ByVal [address] As String)
        Select Case [number]
            Case 1
                If Me.WebBrowser1.InvokeRequired Then
                    Dim d As New NavCb(AddressOf Nav)
                    Me.Invoke(d, New Object() {[number], [address]})
                Else
                    Me.WebBrowser1.Navigate([address])
                End If
            Case 2
                If Me.WebBrowser2.InvokeRequired Then
                    Dim d As New NavCb(AddressOf Nav)
                    Me.Invoke(d, New Object() {[number], [address]})
                Else
                    Me.WebBrowser2.Navigate([address])
                End If
            Case 3
                If Me.WebBrowser3.InvokeRequired Then
                    Dim d As New NavCb(AddressOf Nav)
                    Me.Invoke(d, New Object() {[number], [address]})
                Else
                    Me.WebBrowser3.Navigate([address])
                End If
        End Select
    End Sub

    Private Function wbDocText(ByVal [number] As Integer) As String
        Select Case [number]
            Case 1
                If Me.WebBrowser1.InvokeRequired Then
                    Dim d As New wbDocTextCb(AddressOf wbDocText)
                    Return Me.Invoke(d, New Object() {[number]})
                Else
                    Return Me.WebBrowser1.DocumentText
                End If
            Case 2
                If Me.WebBrowser2.InvokeRequired Then
                    Dim d As New wbDocTextCb(AddressOf wbDocText)
                    Return Me.Invoke(d, New Object() {[number]})
                Else
                    Return Me.WebBrowser2.DocumentText
                End If
            Case 3
                If Me.WebBrowser3.InvokeRequired Then
                    Dim d As New wbDocTextCb(AddressOf wbDocText)
                    Return Me.Invoke(d, New Object() {[number]})
                Else
                    Return Me.WebBrowser3.DocumentText
                End If
            Case Else : Return ""
        End Select
    End Function

    Private Function wbDoc(ByVal [number] As Integer) As HtmlDocument
        Select Case [number]
            Case 1
                If Me.WebBrowser1.InvokeRequired Then
                    Dim d As New wbDocCb(AddressOf wbDoc)
                    Return Me.Invoke(d, New Object() {[number]})
                Else
                    Return Me.WebBrowser1.Document
                End If
            Case 2
                If Me.WebBrowser2.InvokeRequired Then
                    Dim d As New wbDocCb(AddressOf wbDoc)
                    Return Me.Invoke(d, New Object() {[number]})
                Else
                    Return Me.WebBrowser2.Document
                End If
            Case 3
                If Me.WebBrowser3.InvokeRequired Then
                    Dim d As New wbDocCb(AddressOf wbDoc)
                    Return Me.Invoke(d, New Object() {[number]})
                Else
                    Return Me.WebBrowser3.Document
                End If
            Case Else : Return Nothing
        End Select
    End Function
End Class


Все эти примеры (от первого и до последнего) очень познавательны. Кому необходимо понять многопоточность (а именно получение и присвоение значений объектов формы из разных потоков), тот по ним разберётся!

Nord777
Гуру
Гуру
Аватара пользователя
 
Сообщения: 1144
Зарегистрирован: 22.02.2004 (Вс) 13:15
Откуда: Подольск

Re: Форма с тремя браузерами в отдельных потоках

Сообщение Nord777 » 03.04.2009 (Пт) 16:47

Ну и как? Быстрее стало с многопоточностью? :wink:
Какой смысл было городить весь этот огород если ты все действия всё равно переводишь в основной поток?
Microsoft Visual Studio 2008
Microsoft .NET Framework 3.5

euroflock
Обычный пользователь
Обычный пользователь
 
Сообщения: 68
Зарегистрирован: 26.02.2009 (Чт) 12:54

Re: Форма с тремя браузерами в отдельных потоках

Сообщение euroflock » 08.04.2009 (Ср) 15:41

Nord777 писал(а):Ну и как?

Вот теперь всё отлично! Работает так, как и предполагалось.

Nord777 писал(а):Быстрее стало с многопоточностью?

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

Наглядный пример: программа выполняет задачу, при которой автоматически загружается сайт (всего 100 необходимых разных url-ссылок определены в массиве), затем (после полной загрузки) программа ожидает 60 секунд и цикл повторяется заново со следующим сайтом.

1. При выполнении поставленной задачи с использованием однопоточного приложения (сайты грузятся один за другим) на загрузку сайта уходит в среднем от 5 до 10 секунд (во всяком случае при моей скорости соединения с интернетом), но бывают и тайм-ауты (установка тайм-аутов 20 секунд) + 60 секунд = 70 секунд / сайт. Умножаем на 100 сайтов и получаем 7000 секунд (или около двух часов).

2. Используя многопоточное (например 10-ти поточное) приложение (сайты грузятся одновременно в 10-ти потоках) на загрузку сайта уходит в среднем от 5 до 10 секунд (пускай 20 секунд) + 60 секунд = 80 секунд / 10 сайтов. Считаем и получаем не более 800 секунд (или 14 минут).

3. Результат трудно не заметить: прирост скорости почти во столько же раз, сколько используем отдельных потоков.

Nord777 писал(а):Какой смысл было городить весь этот огород если ты все действия всё равно переводишь в основной поток?

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

Nord777
Гуру
Гуру
Аватара пользователя
 
Сообщения: 1144
Зарегистрирован: 22.02.2004 (Вс) 13:15
Откуда: Подольск

Re: Форма с тремя браузерами в отдельных потоках

Сообщение Nord777 » 08.04.2009 (Ср) 17:45

Видимо ты не понял, что создал только видимость многопоточности.
В действительности же, вся работа с компонентом WebBrowser выполняется в основном потоке.
Т.е. строка
Код: Выделить всё
WebBrowser1.BeginInvoke(NavigateProc, WebBrowser1, "http://URL1")
выполняет по сути те же действия, что и
Код: Выделить всё
   Private Sub Nav1(ByVal [address] As String)
        If Me.WebBrowser1.InvokeRequired Then
            Dim d As New Nav1Callback(AddressOf Nav1)
            Me.Invoke(d, New Object() {[address]})
        Else
            Me.WebBrowser1.Navigate([address])
        End If
    End Sub
Вот только твоя процедура выполняется два раза.
Первый вызов идет из другого потока, поэтому срабатывает условие InvokeRequired и происходит еще один вызов этой же процедуры,
но уже в основном потоке. И только теперь выполняется строка Me.WebBrowser1.Navigate([address]).
Итог. Ты написал много лишнего кода. Незачем в данном случае делать многопоточность.

Да, конечно стало быстрее. Программа выполняет определённую цепочку действий в многопоточном режиме почти во столько же раз быстрее, сколько отдельных потоков я задаю для её выполнения.
Видимо ты так и не понял, как работает BeginInvoke и WebBrowser.
Microsoft Visual Studio 2008
Microsoft .NET Framework 3.5

euroflock
Обычный пользователь
Обычный пользователь
 
Сообщения: 68
Зарегистрирован: 26.02.2009 (Чт) 12:54

Re: Форма с тремя браузерами в отдельных потоках

Сообщение euroflock » 08.04.2009 (Ср) 19:15

Nord777 писал(а):Видимо ты не понял, что создал только видимость многопоточности... и не понял, как работает BeginInvoke и WebBrowser.


Почему же не понял. С обработкой потоковых запросов в основном потоке всё предельно ясно.
Моя процедура универсальна: выполняется два раза только если вызов происходит из другого потока, и всего один раз - если из основного.
В чём суть дискуссии? Есть варианты как перевети ВСЮ работу с браузером в отдельном потоке без обращений к основному? Или есть варианты по созданию браузера в потоке во время выполнения, чтобы затем этот поток с ним работал напрямую и не срабатывало InvokeRequired?

euroflock
Обычный пользователь
Обычный пользователь
 
Сообщения: 68
Зарегистрирован: 26.02.2009 (Чт) 12:54

Re: Форма с тремя браузерами в отдельных потоках

Сообщение euroflock » 08.04.2009 (Ср) 19:26

Nord777 писал(а):euroflock
Неудачный пример. Этот компонент не работает в многопоточной среде.


Каким компонентом можно заменить WebBrowser, чтобы он работал в многопоточной среде и при этом была возможность управлять элементами и их значениями на загруженной web-странице?

Nord777
Гуру
Гуру
Аватара пользователя
 
Сообщения: 1144
Зарегистрирован: 22.02.2004 (Вс) 13:15
Откуда: Подольск

Re: Форма с тремя браузерами в отдельных потоках

Сообщение Nord777 » 08.04.2009 (Ср) 19:32

Моя процедура универсальна: выполняется два раза только если вызов происходит из другого потока, и всего один раз - если из основного
Твоя процедура ВСЕГДА будет выполняться два раза. И в чём же тут универсальность?
В чём суть дискуссии?
Обычно, когда человек пишет 50 строк кода вместо 10, я предполагаю, что человек чего-то недопонимает. Я пытаюсь тебе это подсказать. Тем не менее, если у тебя нет желания - можем прервать эту беседу.


Каким компонентом можно заменить WebBrowser, чтобы он работал в многопоточной среде и при этом была возможность управлять элементами и их значениями на загруженной web-странице?
WebBrowser прекрасно работает в одном потоке, без всякого уменьшения скорости.
Может ты всё таки запустишь тот пример, что я написал выше и сравнишь скорость загрузки страниц?
Microsoft Visual Studio 2008
Microsoft .NET Framework 3.5

euroflock
Обычный пользователь
Обычный пользователь
 
Сообщения: 68
Зарегистрирован: 26.02.2009 (Чт) 12:54

Re: Форма с тремя браузерами в отдельных потоках

Сообщение euroflock » 08.04.2009 (Ср) 20:14

Твоя процедура ВСЕГДА будет выполняться два раза.

Не всегда, а только тогда, когда она вызывается из другого потока, когда Me.WebBrowser1.InvokeRequired = True, иначе (когда Me.WebBrowser1.InvokeRequired = False) выполняется только Me.WebBrowser1.Navigate([address]) один раз. Где тут два раза? Чего я не понимаю?

Обычно, когда человек пишет 50 строк кода вместо 10, я предполагаю, что человек чего-то недопонимает. Я пытаюсь тебе это подсказать. Тем не менее, если у тебя нет желания - можем прервать эту беседу.

Наконец-то начинают понимать, что я чего-то недопонимаю... Мне более важен практический совет по сути темы, а не беседа типа: "ты не понял, что создал только видимость многопоточности... которую я тебе и подсказал".

WebBrowser прекрасно работает в одном потоке, без всякого уменьшения скорости. Может ты всё таки запустишь тот пример, что я написал выше и сравнишь скорость загрузки страниц?

Конечно же я запускал тот пример о чём сразу же и написал. Зачем мне этот пример, если он, как сказано в упрёк мне же, не решает мою проблему?

В самом начале я просил помочь мне разобраться с мультипоточностью, а именно: Создаём три потока, причём Поток1 работает только с Браузером1 (и т.д.). В ответ на мою просьбу мне советуют в данном примере не использовать мультипоточность и направляют на обработку потоковых запросов в основном потоке через Invoke, чем в дальнейшем и упрекают, мол я не разобрался... В обработке многопоточных запросов в основном потоке я разобрался (по своему), о чём видно из моего длинного кода.

НО СУТЬ ВОПРОСА НЕ В ЭТОМ!

Мне не нужно работать в одном потоке! Задача изначально предполагает создание трёх (реально должно быть 10..20) НЕЗАВИСИМЫХ потоков, результаты выполнения которых отображаются в элементах формы основного потока (WebBrowser's, TextBox).

Может следует использовать BackgroundWorker? Он позволит организовать независимую многопоточность с использованием браузеров?

Williams
Гуру
Гуру
Аватара пользователя
 
Сообщения: 1280
Зарегистрирован: 06.05.2008 (Вт) 18:35
Откуда: System.Reflection.Williams (увидел себя в зеркале :))

Re: Форма с тремя браузерами в отдельных потоках

Сообщение Williams » 08.04.2009 (Ср) 21:43

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

Nord777
Гуру
Гуру
Аватара пользователя
 
Сообщения: 1144
Зарегистрирован: 22.02.2004 (Вс) 13:15
Откуда: Подольск

Re: Форма с тремя браузерами в отдельных потоках

Сообщение Nord777 » 08.04.2009 (Ср) 21:47

Не всегда, а только тогда, когда она вызывается из другого потока,
Ты не понимаешь, что в твоём коде она всегда вызывается из другого потока! А значит выполняется два раза. Для чего её вызывать из другого потока? Чтобы тут же отправить в основной???

Зачем мне этот пример, если он, как сказано в упрёк мне же, не решает мою проблему?
Так ты обьясни свою проблему. Объясни реальную задачу.
Частенько на форуме встречаются ситуации, когда человеку дают кучу кода, кучу обьяснений, а в конце оказывается, что всё это напрасно, потому что чел не посчитал нужным обьяснить свою задачу более подробно. Для чего тебе столько потоков? Какие действия хочешь в них выполнять? Действительно ли тебе нужно отображать веб-страницы или нужно просто закачать данные и затем их проанализировать? Знаешь, ни у кого нет желания выуживать из тебя ответы.
Microsoft Visual Studio 2008
Microsoft .NET Framework 3.5

euroflock
Обычный пользователь
Обычный пользователь
 
Сообщения: 68
Зарегистрирован: 26.02.2009 (Чт) 12:54

Re: Форма с тремя браузерами в отдельных потоках

Сообщение euroflock » 09.04.2009 (Чт) 21:05

Nord777 писал(а):Действительно ли тебе нужно отображать веб-страницы или нужно просто закачать данные и затем их проанализировать?


Спасибо всем за ответы (но постарайтесь на новичков реагировать адекватно их познаниям).

Да, возможно я в самом начале не совсем корректно поставил задачу, но я это сделат так, как понимал её "со своей колокольни". Пока сам не столкнулся, я действительно не знал, что можно закачивать страницы целиком в строковую переменную гоооооораздо быстрее, чем при использовании WebBrowser'a.

Теперь я без проблем создаю любое разумное (пробовал до 50) количество независимых потоков, в каждом потоке использую HttpWebRequest и HttpWebResponse. В конце выполнения длительной операции загрузки и обработки загруженного сайта в строковой переменной я через Invoke записываю результат работы каждого потока в текстовое поле на форме основного потока. Таким же образом, при необходимости, (через Invoke) я без проблем могу отобразить результат загрузки страницы из строковой переменной в WebBrowser.DocumentText, но при этом скорость работы уже далеко не имитация.

Теперь "присматриваюсь" к регулярным выражениям для быстрой обработки моих загруженных строковых переменных, но что-то чувствую не смогу быстро "въехать" (пока для определения количества тэгов "А" пользуюсь методом content.ToUpper.Split("</A>").Count - 1).

Подскажите мне пожалуйста, каким будет регулярное выражение (хотябы пару строчек кода) для поиска в строковой переменной (аналог WebBrowser.DocumentText.ToUpper) всех подстрок, начинающихся с "<A " и заканчивающихся </A> (аналог свойства .OuterHtml но уже без .ToUpper элемента формы из массива WebBrowser.Document.GetElementsByTagName("A")) и помещения всех их в массив строковых переменных? Я всё правильно сформулировал?
Последний раз редактировалось euroflock 09.04.2009 (Чт) 21:28, всего редактировалось 1 раз.

Williams
Гуру
Гуру
Аватара пользователя
 
Сообщения: 1280
Зарегистрирован: 06.05.2008 (Вт) 18:35
Откуда: System.Reflection.Williams (увидел себя в зеркале :))

Re: Форма с тремя браузерами в отдельных потоках

Сообщение Williams » 09.04.2009 (Чт) 21:26

Код: Выделить всё
"<A " и заканчивающихся </A>


Код: Выделить всё
<A(.*?)</A>


Вроде так, если мне не изменяет память - нструментов проверить под рукой нет.

В Match.Groups(1).Value получишь результат.
И вы думаете, что вас оставят в живых после прочтения этого поста?

Nord777
Гуру
Гуру
Аватара пользователя
 
Сообщения: 1144
Зарегистрирован: 22.02.2004 (Вс) 13:15
Откуда: Подольск

Re: Форма с тремя браузерами в отдельных потоках

Сообщение Nord777 » 09.04.2009 (Чт) 22:05

Код: Выделить всё
      Dim TestStr As String
      TestStr = "<td class=""genmed"" align=""right""><a href=""./faq.php"">" & _
                "<img src=""./styles/subsilver2/theme/images/icon_mini_faq.gif"" " & _
                "width=""12"" height=""13"" alt=""*"" /> FAQ</a></td>" & _
                "<td class=""row1""><p class=""breadcrumbs""><a href=""./index.php"">Список форумов</a> &#187; " & _
                "<a href=""./viewforum.php?f=59"">.NET</a> &#187; <a href=""./viewforum.php?f=2"">Visual Basic .NET</a></p>" & _
                "<p class=""datetime"">Часовой пояс: UTC + 3 часа </p></td>"

      Dim MC As MatchCollection = Regex.Matches(TestStr, "<A.*?</A>", RegexOptions.IgnoreCase)
      MsgBox MC.Count
Microsoft Visual Studio 2008
Microsoft .NET Framework 3.5

euroflock
Обычный пользователь
Обычный пользователь
 
Сообщения: 68
Зарегистрирован: 26.02.2009 (Чт) 12:54

Re: Форма с тремя браузерами в отдельных потоках

Сообщение euroflock » 10.04.2009 (Пт) 10:37

Большое спасибо - всё отлично работает!
Сейчас пытаюсь разбираться с премудростями регулярных выражений, но не хватает доступной комментированной информации (в русской части MSDN есть, а всего понять не могу). С чего начать и куда копать?

Очень не хочется надоедать вопросами, ответы на которые возможно многие уже и забыли - хочется знать всё самому.
Последний раз редактировалось euroflock 10.04.2009 (Пт) 10:39, всего редактировалось 1 раз.

Williams
Гуру
Гуру
Аватара пользователя
 
Сообщения: 1280
Зарегистрирован: 06.05.2008 (Вт) 18:35
Откуда: System.Reflection.Williams (увидел себя в зеркале :))

Re: Форма с тремя браузерами в отдельных потоках

Сообщение Williams » 10.04.2009 (Пт) 10:39

euroflock писал(а):Большое спасибо - всё отлично работает!
Сейчас пытаюсь разбираться с премудростями регулярных выражений, но не хватает доступной комментированной информации (в русской части MSDN есть, а всего понять не могу). С чего начать и куда копать?


Если английским хоть в какой-то мере владеешь, то рекоммендую RegexBuddy, там есть и отладка и туториал
И вы думаете, что вас оставят в живых после прочтения этого поста?

RayShade
Scarmarked
Scarmarked
Аватара пользователя
 
Сообщения: 5511
Зарегистрирован: 02.12.2002 (Пн) 17:11
Откуда: Russia, Saint-Petersburg

Re: Форма с тремя браузерами в отдельных потоках

Сообщение RayShade » 10.04.2009 (Пт) 10:40

Можешь ко мне в ICQ стукнуться, объясню пару нюансов которые важно знать для начала :)
I don't understand. Sorry.

euroflock
Обычный пользователь
Обычный пользователь
 
Сообщения: 68
Зарегистрирован: 26.02.2009 (Чт) 12:54

Re: Форма с тремя браузерами в отдельных потоках

Сообщение euroflock » 10.04.2009 (Пт) 12:00

Что такое регулярные выражения RegEx
Если Вы уже знакомы с регулярными выражениями, то можете смело пропустить этот раздел.

С помощью регулярных выражений вы можете производить поиск, замену подстрок, используя шаблоны. Они состоят из обычных символов и так называемых метасимволов (metacharacters) - управляющих символов. Список метасимволов достаточно обширен. Ниже приведены наиболее часто используемые символы.
    Символ Описание
    * Соответствует выражению, находящемуся до знака "*", взятому ноль или более раз. Например, шаблон "[0-9]*" определяет строку, содержащую ноль или более цифр.
    \ Предназначен для определения символа, являющегося метасимволом. Например, шаблон "." соответствует любому символу, а шаблон "\." будет соответствовать точке.
    ^ Определяет начало входной строки.
    $ Определяет конец входной строки.
    + Соответствует выражению, находящемуся до знака "+", взятому один или более раз. Например, шаблон "[0-9]+" определяет строку, содержащую одну или более цифр.
    . Определяет любой символ кроме символа перевода строки.
    | Разделяет два выражения. Например, шаблону "a|b" будут соответствовать строки "a" и "b".
    [a-z] Определяет диапазон символов. Например, шаблон "[0-9]" определяет цифру.
    [^...] Определяет любой символ, не соответствующий заданному набору. Например, шаблон "[^0-9]" определяет любой символ, кроме цифры.
    \w Слово. То же, что и [a-zA-Z_0-9].
    \W Все, кроме слов. То же, что и [^a-zA-Z_0-9].
    \s Любое пустое место. То же, что и [ \f\n\r\t\v].
    \S Любое непустое место. То же, что и [^ \f\n\r\t\v].
    \d Десятичная цифра. То же, что и [0-9].
    \D Не цифра. То же, что и [^0-9].


Другие метасимволы вы можете найти в MSDN http://msdn.microsoft.com/ru-ru/library/system.text.regularexpressions.aspx. Ниже приведён простой пример регулярного выражения - шаблон номера телефона.

^(\(\d+\)){0,1}\d{3}-\d{2}-\d{2}$

Это выражение может показаться сложным, хотя на самом деле всё просто. Начинаем разбор полётов…
^ - Этот символ указывает, что здесь начинается искомая строка.
( - Код города располагается внутри скобок.
\( - Открывающая скобка.
\d+ - После скобки должен располагаться код города - одна или несколько цифр.
\) - Закрывающая скобка.
) - Закрывающая скобка - метасимвол, указывающий, что здесь заканчивается группируемое выражение (в данном случае, код города).
{0,1} - Этот метасимвол указывает, что выражение в скобках (код города) может повторяться от нуля до одного раза, то есть код города можно не указывать.
\d{3}-\d{2}-\d{2} - Три группы цифр - одна группа по три и две по две цифры. Цифры разделены дефисами.
$ - Этот знак показывает, что здесь заканчивается подстрока.

Класс RegEx

Для работы с регулярными выражениями в VB .NET используется класс RegEx, находящийся в пространстве имён System.Text.RegularExpressions. С помощью этого класса вы можете производить следующие действия:
Поиск подстрок по шаблону
Замена подстрок по шаблону
Сравнение строки с шаблоном
Разделение строки на подстроки с использованием шаблонов.

Для произведения действий с регулярными выражениями необходимо создать экземпляр класса RegEx. Для этого используется стандартный конструктор New. Он перегружен и имеет две комбинации параметров. Вы можете задать только шаблон (переменная типа String), который будет использоваться в дальнейшем, либо шаблон и параметры объекта. Параметры задаются константами из перечисления RegExOptions. Ниже приведены константы и их описания.

    Константа Описание
    Compiled Указывает, что регулярное выражение будет скомпилировано в сборку. Подробнее о компилированных регулярных выражениях читайте ниже.
    ECMAScript Устанавливает ECMAScript-совместимость выражения. Может использоваться совместно с константами IgnoreCase, Multiline и Compiled. При использовании ECMAScript с другими константами будет инициировано исключение.
    IgnoreCase Определяет, что при проведении операций с выражениями, регистр не имеет значения.
    IgnorePatternWhitespace Устраняет пробелы из шаблонов и позволяет использовать комментарии, отмеченные знаком "#".
    Multiline Если этот флаг установлен, то метасимволы "^" и "$" отмечают начало и конец каждой строки (строки разделены знаком перевода строки), а не начало и конец всего выражения.
    None Указывает, что не установлена ни одна опция.
    RightToLeft Устанавливает, что поиск будет производиться с права на лево, а не наоборот.
    SingleLine Указывает, что операции будут производиться в однострочном режиме. Эта константа изменяет значение метасимвола ".". Он определяет любой символ, включая символ перевода строки.

Поиск подстрок

Поиск подстрок, соответствующих шаблону производится с помощью перегруженного метода Matches. Он может принимать 4 комбинации параметров. Первый параметр - строка, в которой будет производиться поиск. В качестве второго параметра можно установить позицию, с которой будет начат поиск. Также вторым параметром можно указать шаблон (если он не совпадает с шаблоном, указанным в конструкторе при создании объекта). И последняя комбинация параметров - строка для поиска, шаблон и параметры поиска, заданные комбинацией констант перечисления RegExOptions.

Метод возвращает объект MatchCollection. Это коллекция, которая содержит объекты Match. Получить объект Match можно с помощью индексированного свойства Item коллекции. Нумерация элементов начинается с нуля. Чтобы получить найденную подстроку, следует использовать свойство Value объекта Macth.

Ниже приведён небольшой пример поиска тэгов в HTML коде.
Код: Выделить всё
Dim regexp As New Regex("<(.*?)>")
Dim html As String
Dim i As Integer
Dim m As MatchCollection

html = "<p>Это <a href='http://vbnet.ru'>пример</a> <b>поиска</b></p>"
m = regexp.Matches(html)
For i = 0 To m.Count - 1
    MsgBox(m.Item(i).Value)
Next


Замена

Другое часто используемое действие, производимая с помощью класса RegEx - замена подстрок с использованием шаблонов. Для замены используется метод Replace. Он, как и метод Matches, перегружен. Replace может принимать 10 комбинаций параметров. Я не буду перечислять здесь все - вы можете найти их в MSDN или в Object Browser. Метод может принимать комбинации из следующих параметров:
    input - Исходная строка.
    replacement - Строка, на которую будут заменены найденные подстроки.
    count - Максимальное количество замен.
    startat - Позиция в строке input, с которой будет производиться замена.
    pattern - Заменяемый шаблон.
    options - Опции. Может принимать константы из перечисления RegExOptions.
    evaluator - Объект MatchEvaluator.

Метод возвращает переменную типа String - строка, в которой были произведены замены.
Код: Выделить всё
Dim regexp As New System.Text.RegularExpressions.Regex("<(.*?)>")
Dim InputString As String

InputString = "p>Это <a href='http://vbnet.ru'>пример</a> <b>pfvtys</b></p>"
txtText.Text = regexp.Replace(txtText.Text, "[вырезано]", RegexOptions.Multiline)


Сравнение

Сравнение строки с шаблоном - самая простая операция, которую можно произвести с помощью класса RegEx. Сравнение осуществляется методом IsMatch. Он перегружен и может принимать такие же параметры, как и метод Matches. Возвращаемое значение имеет тип Boolean. Метод возвращает True, если тестируемая строка совпадает с шаблоном и False в противном случае. Ниже приведён пример сравнения строки с шаблоном.

Код: Выделить всё
Dim regexp As New System.Text.RegularExpressions.RegEx ("[0-9]+")
Dim str As String

str = "1234567890"
MsgBox (regexp.IsMatch(str).ToString)
str = "abc"
MsgBox (regexp.IsMatch(str).ToString)


Разделение строки

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

Для разделения строки используется перегруженный метод Split класса RegEx. Он может принимать такие же комбинации параметров, как и методы Matches и IsMatch. Split возвращает массив типа String, который содержит строки, полученные из исходной строки. Массив индексируется с нуля.

Приведу небольшой пример. Допустим, вам нужно разбить строку по нескольким разделителям (скажем, "-", "." и ","). Можно использовать для этого функцию Split, но при этом будет много возни с массивами, код будет сильно загромождён и снизится быстродействие. А теперь посмотрим, как легко эта операция будет произведена с использованием регулярных выражений. Разделителем будет являться следующее выражение: "[-\.,]". Следующий код разбивает строку на подстроки с использованием этого шаблона.

Код: Выделить всё
Dim regexp As New Regex("[-\.,]")
Dim i As Integer
Dim s() As String
Dim str As String

str = "раз-два,три.четыре,пять"
s = regexp.Split(str)
For i = 0 To s.GetUpperBound(0)
    Console.WriteLine(s(i))
Next


Компилированные регулярные выражения

По умолчанию RegEx компилирует регулярные выражения в последовательность внутренних байт-кодов регулярных выражений (это высокоуровневый код). При выполнении регулярных выражений происходит интерпретация байт-кода.

Если при создании объекта RegEx в конструкторе New была установлена константа Compiled, то RegEx будет скомпилирован в MSIL (Microsoft intermediate language). Это позволит JIT-компилятору преобразовать выражение в машинный код, что значительно повышает производительность.

Однако в компилированных выражениях есть и плохая сторона - их нельзя выгрузить из памяти. Компилированные регулярные выражения выгружаются только при завершении работы всего приложения. RegEx остается в памяти, даже когда сам объект освобождён и уничтожен сборщиком мусора.

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

Заключение

Регулярные выражения - мощная технология для работы с текстом. Радует то, что разработчики Microsoft всё-таки встроили поддержку этой технологии в .NET Framework. Возможно, они сделали это потому что .NET Framework используется в Web-приложениях (ASP .NET), где регулярные выражения больше всего необходимы.

Автор статьи Westry http://rudocs.com/content/view/381/1/

euroflock
Обычный пользователь
Обычный пользователь
 
Сообщения: 68
Зарегистрирован: 26.02.2009 (Чт) 12:54

Re: Форма с тремя браузерами в отдельных потоках

Сообщение euroflock » 02.05.2009 (Сб) 0:43

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

Да, встроенный в студию контрол WebBrowser не позволяет создавать его экземпляр в потоке для дальнейшей безопасной обработки без InvokeRequired, с которым (т.е. с делегатом через InvokeRequired) получается только иммитация многопоточности или псевдо-мультипоточность.

Далее, реализовать все необходимые программные обработки (событий, поведений, действий и т.п.) загруженного при помощи HttpWebRequest/Response контента страницы сайта с таким же ожидаемым эффектом, как если бы этот контент страницы был загружен в окно браузера просто невозможно. Невозможно хотябы только потому, что нет возможности получить результат выполнения скриптов на странице. Невозможно также из-за того, что представление самого контента (вид документа HTML-кода) в браузере заметно отличается от оригинального, полученного от HttpWebRequest/Response, т.к. браузер всегда получает и "по-своему" его преобразовывает, в связи с чем например .OuterHTML любого тэга из браузера WebBrowser всегда (почти всегда) будет отличаться от "пропарсинного регэксом" из оригинала.

Всё оказалось очень просто!

1. Подключаем стандартную библиотеку класса Microsoft Internet Explorer:
Код: Выделить всё
Imports SHDocVw


2. Внутри выполняющегося потока создаём "виртуальный" браузер (созданный в текущем потоке), с которым работаем напрямую, как с обычным браузером (точно такие же методы и свойства) в однопоточном приложении:
Код: Выделить всё
        ...
        Dim ВиртуальныйБраузер1 As New InternetExplorer
        ВиртуальныйБраузер1.Navigate(address) ' Dim address As String = "http://что-то.типа/этого"
        While Not ВиртуальныйБраузер1.ReadyState = tagREADYSTATE.READYSTATE_COMPLETE
            Application.DoEvents()
        End While
        Dim Документ1 As mshtml.HTMLDocument = ВиртуальныйБраузер1.Document
        Dim ТекстДокумента1 As String = Документ1.documentElement.innerHTML
        ... ' и так далее в таком же духе


3. И это ещё не всё! Каждый такой "виртуальный" браузер представляет собой реальный, запущенный в отдельном окне, экземпляр системного Internet Explorer, увидеть ход либо результат загрузки или обработки страницы в котором можно в любой момент выполнения (также при необходимости не составит труда передать контент "виртуального" браузера в имеющийся на форме WebBrowser, но уже с использованием делегата):
Код: Выделить всё
        ...
        ВиртуальныйБраузер1.Visible = True
        ...


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

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

Сейчас этот форум просматривают: Mail.ru [бот] и гости: 74

    TopList