ProgressBar freezes when using multithreading

2.7k views Asked by At

I have a ProgressBar that uses the marquee style when a report is being generated. The reason I am doing this is because the ReportViewer control I use takes some time to generate the report thus making the form unresponsive. I generate the report using a thread so the ProgressBar can show that the program is working. However, when I start the thread the ProgressBar freezes. I have already tried the BackgroundWorker but that didn't work so I used my own threading.

The reason I use the Invoke() method is because I can't make changes to the ReportViewer control on the thread I created because it was created on the UI thread.

The method that takes the most time processing is the RefreshReport() method of the ReportViewer control which is why I'm trying to do that on its own thread instead of the UI thread.

Any help would be appreciated. Thanks.

Here is the code for my thread variable:

Private t As New Thread(New ParameterizedThreadStart(AddressOf GenerateReport))

Here is the code for the button that generates the report:

Private Sub btnGenerateReport_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles btnGenerateReport.Click
    pbReports.Style = ProgressBarStyle.Marquee

    If t.ThreadState = ThreadState.Unstarted Then
        t.IsBackground = True
        t.Start(ReportType.Roads)
    ElseIf t.ThreadState = ThreadState.Stopped Then
        t = Nothing
        t = New Thread(New ParameterizedThreadStart(AddressOf GenerateReport))
        t.IsBackground = True
        t.Start(ReportType.Roads)
    End If
End Sub

Here is the code that generates the report:

Public Sub GenerateReport(ByVal rt As ReportType)
    If rvReport.InvokeRequired Then
        Dim d As New GenerateReportCallBack(AddressOf GenerateReport)
        Me.Invoke(d, New Object() {rt})
    Else
        rvReport.ProcessingMode = ProcessingMode.Remote
        rvReport.ShowParameterPrompts = False
        rvReport.ServerReport.ReportServerUrl = New Uri("My_Report_Server_URL")
        rvReport.ServerReport.ReportPath = "My_Report_Path"
        rvReport.BackColor = Color.White

        rvReport.RefreshReport()
    End If

    If pbReports.InvokeRequired Then
        Dim d As New StopProgressBarCallBack(AddressOf StopProgressBar)
        Me.Invoke(d)
    Else
        StopProgressBar()
    End If
End Sub
2

There are 2 answers

0
Jon Skeet On BEST ANSWER

Your code is starting a new thread from the UI thread. The new thread then immediately marshals back to the UI thread using Invoke - so basically it's as if you hadn't made it multithreaded at all.

Instead of that, make the new thread do all the background processing it can and only marshal back to the UI for parts of the process that need to update the UI.

0
Noah On

You can try using the ThreadPool to generate a new worker thread. I use the below in a WPF application to show a loading screen for anything that takes over 4 seconds or so.

You might need to change some of the syntax as I'm a C# guy...

Private Sub btnGenerateReport_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles btnGenerateReport.Click
    pbReports.Style = ProgressBarStyle.Marquee

    ThreadPool.QueueUserWorkItem(Function(th) Do
        GenerateReport(ReportType.Roads)
        Dispatcher.BeginInvoke(DispatcherPriority.Normal, DirectCast(Function() Do
            StopProgressBar()
        End Function, Action)
    End Function)

End Sub

Also, I believe that the Dispatcher.BeginInvoke is only in WPF and not in WinForms, so you my need to change that back to Me.Invoke or something.