Потокобезопасный вызов проблем со списком

Первый пост здесь. Хотя долго скрывается. Я займусь этим.

Мой небольшой побочный проект — это приложение, которое будет собирать сообщения с Craigslist. После очистки данные листинга отправляются в список в «Form1». Я создал рабочий класс для обработки всей очистки. Проблема возникает, когда моя функция класса "guiAdd()" не будет правильно заполнять список.

Данная форма Form1 существует, и в ней есть элемент управления списком с именем "lvsearch":

Вот где я вижу проблему: Подпрограмма класса

Private Sub guiAdd(ByVal data As String)
    If Form1.lvsearch.InvokeRequired Then
        Form1.lvsearch.Invoke(New Action(Of String)(AddressOf guiAdd), data)
    Else
        Dim fitem As New ListViewItem
        fitem.Text = data
        'Form1.lvsearch.Items.Add(fitem) <---Original Version
        Form1.lvsearch.Items.Add(New ListViewItem("WTF!!!!")) '<--- Sanity Version
    End If
End Sub

Вот весь класс:

Imports System.Threading
Imports System.Threading.Thread
Imports System.IO
Imports System.Net

Public Class craigsearcher
    Private cURL As String 'Class scope variables preceded by "c"
    Private cSER As Integer





Public Sub New(ByVal fURL As String, Optional ByVal autoStart As Boolean = True)
    Try
        cURL = fURL 'Function scope variables preceded by "f"
        serialGen()
        If autoStart = True Then
            invokeSearch()
        End If
    Catch ex As Exception
        MessageBox.Show(ex.Message)
    End Try
End Sub





Private Sub invokeSearch()
    Dim cTHREAD As New Threading.Thread(AddressOf search)
    cTHREAD.IsBackground = True
    cTHREAD.Start()
End Sub





Private Sub serialGen()
    'Random number to track this threads temp files

    Dim fRND As Double
    Dim fINT As Integer

    Randomize()
    fRND = Rnd() * 1000000000
    fINT = Math.Floor(fRND)
    cSER = fINT.ToString
End Sub






Private Sub search()
    'Class Workflow

    prepData()
    extractData()
    'MessageBox.Show("Debug: End of Thread")
End Sub





Private Sub prepData()
    'WIP: This is just proof of concept currently.
    'Needs revision, but get the job done for now.

    Dim client As New WebClient()
    Dim rawHTML As String = client.DownloadString(New Uri(cURL))


    Dim fo As New StreamWriter(".\temp\" & cSER & ".dat")
    fo.WriteLine(rawHTML)
    fo.Close()
    Thread.Sleep(25)
    Dim fo2 As New StreamReader(".\temp\" & cSER & ".dat")
    Dim fo2str As String = ""
    Do Until fo2.EndOfStream = True
        fo2str = fo2str & Trim(fo2.ReadLine()) & vbCrLf
    Loop
    fo2.Close()
    Thread.Sleep(25)

    System.IO.File.Delete(".\temp\" & cSER & ".dat")

    Dim fo3 As New StreamWriter(".\temp\Prep" & cSER & ".dat")
    fo3.WriteLine(fo2str)
    fo3.Close()
    Thread.Sleep(25)
End Sub





Private Sub extractData()
    'WIP: This is just proof of concept currently.

    Dim fo As New StreamReader(".\temp\Prep" & cSER & ".dat")
    Dim fstr As String = ""

    Do Until fo.EndOfStream = True
        fstr = fo.ReadLine()

        If InStr(fstr, "<p class=") Then 
            'FUTURE LOGIC AND STUFFS SORTED HERE. Regex etc.
            guiAdd(fstr)
        End If

    Loop
End Sub





Public Sub guiAdd(ByVal data As String)
    If Form1.lvsearch.InvokeRequired Then
        Form1.lvsearch.Invoke(New Action(Of String)(AddressOf guiAdd), data)
    Else
        Dim fitem As New ListViewItem
        fitem.Text = data
        'Form1.lvsearch.Items.Add(fitem) <---Original Version
        Form1.lvsearch.Items.Add(New ListViewItem("WTF!!!!")) '<--- Sanity Version
    End If
End Sub

End Class

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

 Dim worker As New craigsearcher("http://atlanta.craigslist.org/ggg/", True)

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

Я использовал это как руководство: http://www.vbforums.com/showthread.php?682082-Understanding-Multi-Threading-in-VB-Net


person Wariv    schedule 24.05.2014    source источник


Ответы (2)


Попробуйте пересмотреть этот метод:

Private Sub search()
    'Class Workflow

    prepData()
    extractData()
    'MessageBox.Show("Debug: End of Thread")
End Sub

к этому

Private Sub search()
    'Class Workflow

    Thread.QueueUserWorkItem(prepData)
    Thread.QueueUserWorkItem(extractData)
    'MessageBox.Show("Debug: End of Thread")
End Sub

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

Надеюсь, поможет.

person Israel Ocbina    schedule 24.05.2014
comment
Спасибо Израиль. Когда я пробую этот метод, я получаю исключение IO.FileNotFound, выдаваемое функцией extractData(). Вопреки своему предназначению, prepData() не завершает создание необходимых файлов, необходимых для extractData(). Тем не менее, я собираюсь провести еще несколько исследований. - person Wariv; 24.05.2014
comment
Извините за это, я думал, что ваш код асинхронный. Вы можете попробовать сделать это с помощью System.Thread.Task. Вы можете попробовать реализовать свои коды на асинхронном шаблоне. Попробуйте изменить выше, как это: - person Israel Ocbina; 26.05.2014
comment
Task.Factory.StartNew(Sub() prepData()).ConitnueWith(Sub() extractData()) Вы можете попробовать посмотреть msdn.microsoft.com/en-us/library/ если вы не знакомы с шаблоном библиотеки параллельных задач. - person Israel Ocbina; 26.05.2014

Вы тестировали это без отдельного рабочего/потока?

это должно быть в вашей форме, а не в вашем рабочем классе/потоке:

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

Private Delegate Sub invokeGuiAdd(ByVal data As String)
Public Sub guiAdd(ByVal data As String)
    If InvokeRequired Then
        'Form1.lvsearch.Invoke(New Action(Of String)(AddressOf guiAdd), data)
        Invoke(New invokeGuiAdd(AddressOf guiAdd), New Object() {data})
    Else
        Dim fitem As New ListViewItem
        fitem.Text = data
        'Form1.lvsearch.Items.Add(fitem) <---Original Version
        lvsearch.Items.Add(New ListViewItem("WTF!!!!")) '<--- Sanity Version
    End If
End Sub

и

Form1.guiAdd(fstr)

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

person porkchop    schedule 24.05.2014
comment
Спасибо за ответ и ваше время. Я не спал, пробуя множество различных методов реализации делегатов. Однако результат тот же. На данный момент я думаю, что просто буду хранить данные в переменной класса и обращаться к ним из Form1, когда рабочий поток завершится. Что меня действительно беспокоит, так это то, что несколько недель назад я получил очень похожую реализацию для работы в другом стороннем проекте, но у меня больше нет этого исходного кода :( - person Wariv; 24.05.2014