GUI上的多线程动作顺序

时间:2017-02-14 20:40:35

标签: vb.net multithreading

我目前正在编写一个使用WGET从不同服务器下载多个csv-Files的程序。每次下载都是一个新线程,因为我使用WaitForExit(10000)方法将WGET称为进程,因此WGET有10秒的时间下载。如果下载没有在该时间内完成,则线程被杀死,因为服务器没有及时回答。

此外,还有一个列表视图,记录我的程序当前正在执行的操作以及哪个线程以哪种状态结束。

这是我记录的方法(lvw_log是我的ListView):

Public Delegate Sub LogDelegate(ByVal Text As String)

Public Sub Log(Text As String)
    If lvw_Log.InvokeRequired Then
        lvw_Log.BeginInvoke(New LogDelegate(AddressOf Log), New Object() {Text})
    Else
        lvw_Log.Items.Add(DateTime.Now + ": " + Text)
        lvw_Log.TopIndex = lvw_Log.Items.Count - 1
        lvw_Log.Refresh()
    End If

End Sub

当必须从其中一个WGET-Threads将文本添加到我的ListView时,将调用该委托。 'p'是一个自己类的对象,用于移交一组可供线程访问的变量。

我将每个线程存储在一个名为WGETThreadArray的ArrayList中:

Dim WGETThreadArray As New ArrayList

For i = 0 to NumberOfFilesToDownload - 1
  Dim WGETThread As New System.Threading.Thread(AddressOf StartWGET)

  WGETThreadArray.Add(WGETThread)
  Log("Starting thread " + i.ToString)
  WGETThreadArray(i).Start(p)
Next

现在我想等待所有线程完成或中止:

Log("Waiting for threads to finish")
For i = 0 To WGETThreadArray.Count - 1

  WGETThreadArray(i).Join()

Next
    Log("All threads closed")
    Log("Downloaded all DB-Info-Files")

线程(方法StartWGET)是这样的:

Public Sub StartWGET(p As Object) 
    'this method is called for each thread to parallely download the necessary files

    Dim procInfo As New ProcessStartInfo(p.PathToWgetExe, p.ArgumentString)
    procInfo.CreateNoWindow = False
    procInfo.UseShellExecute = True

    Dim WGETProcessHandler As System.Diagnostics.Process = System.Diagnostics.Process.Start(procInfo)

    If Not WGETProcessHandler.WaitForExit(10000) Then         'if WGET doesn't finish within '10000' milliseconds, the thread gets killed

        WGETProcessHandler.Kill()
        Log("DB " + p.DBName + " was not loaded. Thread " + p.ThreadIndex.ToString + " killed ")
        DatabaseArray(p.ThreadIndex).isLoaded = False
        WGETThreadArray(p.ThreadIndex).Abort()

    Else
        DatabaseArray(p.ThreadIndex).isLoaded = True
        Log(p.URL + " downloaded. Thread " + p.ThreadIndex.ToString + " ended successfully.")

    End If
End Function

如您所见,在线程中调用方法“Log”。问题是主线程总是在其他线程之前写入ListView。所以我在“线程已成功结束”之类的消息之前看到“所有线程已关闭”行。虽然我在for-Loop中使用了.Join()方法。我想让它等到所有线程都完成。

那么在完成所有其他WGET-Thread之前,如何让主线程暂停。而且,如何在主线程接管之前让它们记录并告诉我所有线程都已完成。

我知道很难解释,我真的希望自己清楚明白。如果没有,请再次问我,这样我就能更好地解释自己。

2 个答案:

答案 0 :(得分:0)

主要线程被Join()调用阻塞,因此尝试切换到主线程的Log的BeginInvoke必须等待主线程变为可用(在所有连接之后)这意味着实际在主线程可用后,将执行对Log的调用。那是在等待线程并记录"所有线程关闭后#34;消息

在新线程上调用等待工作线程以释放主线程以执行 UI呈现。这是一个重要的原则:拥有一个响应式UI,让主线程只做很少的工作,永远不会阻止它。

答案 1 :(得分:0)

 Log("Waiting for threads to finish")
 Dim SomeAlive as boolean
 do 
    Threading.Thread.Sleep(100)
    SomeAlive = False
    For i as integer = 0 To WGETThreadArray.Count - 1
       if WGETThreadArray(i).IsALive then 
           SomeALive = True
           Exit For
        end if
    Next
Loop While SomeAlive

你的发言

 WGETThreadArray(p.ThreadIndex).Abort()

毫无意义,因为线程无论如何都会在例程结束时中止。

同时产生多个进程来下载多个文件可能不会让你一次只做一个。