Search code examples
vb.netmultithreadingcopyblocking

Visual Basic, Child Thread Blocking Main Thread


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

Solution

  • 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