1

I've seen a ton of post regarding the problem of control.Invoke hanging applications but they mostly seemed to be restricted to those running on .NET 1.1. I've also seen suggestions to use .BeginInvoke instead which I have tried but to no avail. .BeginInvoke doesn't hang but it also doesn't call the delegate. My main concern is that I have used .Invoke for years with no issues and I have a major application in the field that uses it extensively and I'm worried that this problem will crop up there. I am doing nothing differently as far as I can tell between my working code and the code that fails. I've written up a dead simple bit of code that replicates the issue (all in 4.0 VS2010):

Public Class Form1

    Private WithEvents sat As TestInvoke
    Private Delegate Sub doTheUpdateDelegate(ByVal message As String)

    Private Sub Button1_Click(sender As System.Object, e As System.EventArgs) Handles     Button1.Click
        sat = New TestInvoke

        sat.startAThread()

    End Sub

    Public Class TestInvoke

        Public Event UpdateControl()

        Public Sub startAThread()
            Dim t As New Threading.Thread(AddressOf _startAThread)
            Dim trace As String

            trace = "a"
            t.SetApartmentState(Threading.ApartmentState.STA)

            t.Start()
            t.Join()

        End Sub

        Protected Sub _startAThread()
            Try
                For k = 0 To 10
                    System.Threading.Thread.Sleep(1000)
                    k += 1
                    RaiseEvent UpdateControl()
                Next
            Catch ex As Exception
                MsgBox(ex.Message)
            End Try
        End Sub
    End Class

    Private Sub sat_UpdateControl() Handles sat.UpdateControl
        Try
            Call doTheupdate(Now.ToString)
        Catch ex As Exception
            MsgBox(ex.Message)
        End Try
    End Sub

    Private Sub doTheUpdate(ByVal message As String)
        Try
            If Button1.InvokeRequired = True Then
                Dim objParams As Object() = {message}

                'hangs on the following line
                Button1.Invoke(New doTheUpdateDelegate(AddressOf doTheUpdate),     objParams)

            Else
                Button1.Text = message
            End If
        Catch ex As Exception
            MsgBox(ex.Message)
        End Try
    End Sub
End Class

If anyone can see what I've done wrong here I'd really appreciate it!

Peter Oram
  • 6,213
  • 2
  • 27
  • 40
eric_the_animal
  • 432
  • 9
  • 19

2 Answers2

12

There's only one reason a Control.Invoke() call would hang. Or a BeginInvoke() call not executing its target, same thing. It happens when the main thread of the program, the UI thread, is not idle and busy doing something else.

What the "something else" could be is all over the map. The worst thing you could do have the main thread wait for the worker thread to complete. That's a guaranteed deadlock if you use Invoke().

The condition is very easy to diagnose, use Debug + Break All and Debug + Windows + Threads. Double-click the Main thread and look at the Call Stack window. The top of the stack trace should say "Managed to Native Transition", the one below it should be FPushMessageLoop(). If you see something else then you've found the code that causes the deadlock.

Hans Passant
  • 922,412
  • 146
  • 1,693
  • 2,536
  • Thanks very much for the answer - I've not used that method of debugging before and it certainly did indicate that it was hanging on the join. The problem is that I need that join to prevent multiple threads running together updating and inserting data all over the place. I still don't see the difference between my working application that routinely pumps messages to the UI via events within the threaded functions and the one I posted that fails. Frustrating. – eric_the_animal Oct 26 '12 at 01:57
  • I figured out why my currently deployed application does not have this problem. The various routines that call the threaded functions are themselves running in a thread (timer actually) on the main form. This is why the UI is still free to process the messages, it is the thread 'above' it that would not be able to. The key to figuring this out is to step away from the computer, talk to your wife about anything but programming, play some Lego with your kid and get some great feedback on the web. Thanks again for all of your help! – eric_the_animal Oct 26 '12 at 02:40
  • 1
    I like simple Backgroundworkers to avoid that kind of problem. – Christian Sauer Oct 26 '12 at 08:24
  • Yes, calling Thread.Join() invokes the exact deadlock I warned about with "worst thing you can do". A BackgroundWorker doesn't solve it, you also get deadlock from spinning on its IsBusy property. – Hans Passant Oct 26 '12 at 09:27
  • What if the top of the main thread callstack does have FPushMessageLoop() but the Invoke in the worker thread still hangs? – Adam Bruss Sep 16 '16 at 21:05
1

You are using Thread.Join to let the UI thread pause until the other thread is done. At the same time you use Control.Invoke to let the UI thread do some action. This won't work because the UI thread is waiting for the other thread to finish.

I would suggest you either remove the Thread.Join call or do your action in the UI thread if you want it to wait for the action to finish.

Jan-Peter Vos
  • 3,157
  • 1
  • 18
  • 21
  • Thanks for the answer it was helpful but I'm still confused as to why .join works in my existing application. I start a thread eg. t.start then .join and that thread runs a function that pumps multiple messages to the UI without any problems. I think I still need to use Join because I need to allow the thread to complete before I go off and do anything else. – eric_the_animal Oct 26 '12 at 01:45