Visual Basic, Child Thread Blocking Main Thread

2019-03-02 03:17发布

问题:

I've run into a little issue with a simple file copying application I'm writing in Visual Basic 2005. I have a main thread which looks after the GUI, and for the file scanning/copying I've created a separate thread which I create like this:

    trd_copy = New Thread(AddressOf CopyTask)
    trd_copy.IsBackground = True
    trd_copy.Start()

This works fine for the scanning phase of the operation, and I can use the buttons in the GUI just fine. The problem is that when CopyTask gets to the file copying phase (using File.Copy), the main thread appears to lock up, and the GUI with it, meaning that the button I have there for aborting the copy operation is useless. When the copying is done, all returns to normal, and during copying the sub thread can update the status bar on the main form.

I'm sure I'm missing something simple, but I can't for the life of me see what it is.

Thanks very much!

Edit: Adding the code for CopyTask():

Private Sub CopyTask()
    Control.CheckForIllegalCrossThreadCalls = False
    If check_scanfirst.Checked Then
        status1.Text = "Scanning..."
        bytestocopy = 0
        scandir(src)

        filesscanned = True
        MsgBox("Scanning completed")
    End If

    If check_delete.Checked Then
        ' Do a clean of the destination, removing any files that don't exist in the source dir
    End If

    If filesscanned Then
        ProgressBar1.Visible = True
        ProgressBar1.Minimum = 0
        ProgressBar1.Maximum = 100
        ProgressBar1.Refresh()
    End If

    checkdir(src)
    MsgBox("Copying completed")
    If filesfailed > 0 Then
        MsgBox("Warning: " + Str(filesfailed) + " files were not copied successfully.")
    End If
    guistop()
End Sub

回答1:

From what i can tell all calls to the System are also GUI Thread based no matter if you want them or not. And Control.CheckForIllegalCrossThreadCalls = False is very bad, you always want to write your thread and ( if beginner ) run code and everytime it breaks out to code to write a delegate and an invoke function for that part of your thread ( taking up the most minimal time ) one can in the Gui(Main) thread.

Here is an example with full sourcecode

http://www.codeproject.com/Articles/15104/Multithreading-with-VB-NET-A-beginner-s-choice

in your thread

Control.CheckForIllegalCrossThreadCalls = False -- this needs removed If check_scanfirst.Checked Then -- this needs a delegate and invoke method status1.Text = "Scanning..." -- this needs a delegate and invoke method bytestocopy = 0 scandir(src) -- if scandir(has calls to gui )this needs a delegate and invoke method but inside of Scandir()

    filesscanned = True
    MsgBox("Scanning completed")              -- this needs a delegate and invoke method ( keep in mind thread will keep running even if mesgbox not clicked )
End If

If check_delete.Checked Then              -- this needs a delegate and invoke method
    ' Do a clean of the destination, removing any files that don't exist in the source dir
End If

If filesscanned Then
    ProgressBar1.Visible = True             -- this needs a delegate and invoke method
    ProgressBar1.Minimum = 0             -- this needs a delegate and invoke method
    ProgressBar1.Maximum = 100             -- this needs a delegate and invoke method
    ProgressBar1.Refresh()             -- this needs a delegate and invoke method
End If

checkdir(src)
MsgBox("Copying completed")             -- this needs a delegate and invoke method
If filesfailed > 0 Then
    MsgBox("Warning: " + Str(filesfailed) + " files were not copied successfully.")             -- this needs a delegate and invoke method
End If
guistop()             -- this needs a delegate and invoke method if (guistop makes calls to gui )

so if you can't tell your making lots of calls to the gui... heres how i would write it to make it really simple

how i would write your code ( wrote this in word some code may need a tweak )

Private sub DoStuffBeforeCopy()
If check_scanfirst.Checked Then
    status1.Text = "Scanning..."
    bytestocopy = 0
trd_copy.ParameterizedStart(src) //start thread
end sub

CopyTask(byval src as *string*?)
    scandir(src) – put the code here or make another thread ( src is better )
    filesscanned = True

    invoke-MsgBox("Scanning completed")

    invoke If check_delete.Checked Then

    If filesscanned Then
        Invoke-ProgressBar1.Visible = True
        Invoke-ProgressBar1.Minimum = 0
        Invoke-ProgressBar1.Maximum = 100
        Invoke-ProgressBar1.Refresh()
    End If

    checkdir(src) – put the code here or make another thread ( src is better )

    invoke-MsgBox("Copying completed")

    If filesfailed > 0 Then
        Invoke-MsgBox("Warning: " + Str(filesfailed) + " files were not copied successfully.")
    End If

    guistop()– put the code here or make another thread ( src is better )

End sub

** DO THIS FOR ANY Delegat / invoke needed

for threads calls with parameters

trd_copy.ParameterizedStart(src)

Delegate Sub nameofDelegate(s As Integer)
Sub nameofDelegate+NameofSub(ByVal s As Integer)
    If Form1.ProgressBar1.InvokeRequired Then
        Dim d As New nameofDelegate (AddressOf nameofDelegate+NameofSub)
        NameOfYourForm.Invoke(d, New Object() {s})
    Else
        If s = 1 Then
            NameOfYourForm.ProgressBar1.Refresh() ** Or other Gui Functions
        Else

        End If
    End If
End Sub

For Thread calls without parametrs

trd_copy.Start()

Delegate Sub nameofDelegate()
Sub nameofDelegate+NameofSub()
    If Form1.ProgressBar1.InvokeRequired Then
        Dim d As New nameofDelegate (AddressOf nameofDelegate+NameofSub)
        NameOfYourForm.Invoke(d, New Object())
    Else
       NameOfYourForm.ProgressBar1.Refresh() ** Or other Gui Functions
    End If
End Sub