1

How InvokeRequired and Invoke let us make our apps thread safe.

Let's consider such code:

    private void ThreadSafeUpdate(string message)
    {
        if (this.textBoxSome.InvokeRequired)
        {
            SetTextCallback d = new SetTextCallback(msg);
            this.Invoke
                (d, new object[] { message });
        }
        else
        {
            // It's on the same thread, no need for Invoke
            this.textBoxSome.Text = message;
        }
    }
  1. Is it possible to change state of InvokeRequired after InvokeRequired and before Invoke? If not, then why?

  2. How does Invoking make it thread safe?

  3. If InvokeRequired illustrate is current thread owning control, how would the thread know that it is or it is not the owner.

  4. Let's consider that SomeMethod() is currently running on Thread1. We would like to call it from Thread2. Internally this method updates some field. Does Method.Invoke contain some kind of lock mechanism internally?

  5. What if SomeMethod() takes very long time and we would like to run something other on the control owner thread. Does Invoking lock the owner thread or is it some kind of a background thread safe task?

    ThreadSafeUpdate() //takes 5 minutes in Thread2
    ThreadSafeUpdate() //after 2 minutes, we are running it in other thread2
    ThreadSafeUpdate() //next run from Thread3
    
  6. I think it is some kind of general pattern which can be implemented outside of winforms, what's its name?

PiotrWolkowski
  • 8,408
  • 6
  • 48
  • 68
Puchacz
  • 1,987
  • 2
  • 24
  • 38
  • 3
    1. You should split down your question into multiple questions. 2. Why do you think `Invoke` makes it thread-safe? `Invoke` is responsible for queuing work on the UI thread, so that thread-affine UI elements can be accessed. – Yuval Itzchakov Jan 27 '15 at 12:31
  • You should use events to communicate between threads. – user743414 Jan 27 '15 at 12:37
  • 1
    @user743414 You might want to be a bit more specific. There's nothing inherently thread-safe or cross-thread about events. – Luaan Jan 27 '15 at 15:57
  • @Luaan When you have to use InvokeRequired something else is wrong (or at least not good designed). That's by the fact that you're trying to change your UI thread from another thread. The UI thread should show data nothing more. Your worker threads (whatever they do) should manipulate data objects and not the UI directly. The UI can be notified by an event for example to show the modified data. Making the data objects threadsafe should be easy. – user743414 Jan 28 '15 at 08:29
  • @user743414 `InvokeRequired` is code smell (as I've outlined in my answer). But your solution doesn't actually help - you still need to handle proper synchronization for the data. If your worker thread invokes an event, the handler will still run on the worker thread. `Invoke` at least makes sure you can pretend your controls are actors, making sure the commands are serialized on a single thread. I'm not advocating the use of `Invoke`. I'm not saying the worker thread should access the UI directly, but it does need to be able to send data in a thread-safe way to whoever does the UI updates. – Luaan Jan 28 '15 at 08:45
  • @Luaan My comment implies the he needs another design (split work between UI and worker, communication through events than direct access to the objects [UI Controls and data objects]). When using such an design he just have to synchronize writes to his data objects from his worker threads. But he must not synchronize access from his UI thread to his data objects because the UI just shows data. And get signaled, for example by an event, that there is something modified to show. The only reason to synch access from the UI thread would be if he want's to modify his processed data while processed. – user743414 Jan 28 '15 at 09:50
  • But then he have to change more than just splitting the work between UI and worker threads. – user743414 Jan 28 '15 at 09:52
  • @user743414 Not really. You need to synchronize both reads and writes (unless you're using thread-safe structures exclusively and properly). And you still need to marshall the event handler execution to the UI thread somehow. You didn't avoid the need for synchronization, you just moved it to nicer places - a good step in making a decent multi-threaded program, but not enough on its own. Events do nothing whatsoever to help with multi-threading on their own, you still need marshalling and synchronization. Even `Task`s eventually use something like `Invoke` for the final marshalling. – Luaan Jan 28 '15 at 10:02

2 Answers2

3

Is it possible to change state of InvokeRequired

Yes, and it is a pretty common occurrence. Either because you started the thread too soon, before the form's Load event fired. Or because the user closed the window just as this code is running. In both cases this code fails with an exception. InvokeRequired fails when the thread races ahead of the window creation, the invoked code fails when the UI thread races ahead of the thread. The odds for an exception are low, too low to ever diagnose the bug when you test the code.

How Invoking make it thread safe?

You cannot make it safe with this code, it is a fundamental race. It must be made safe by interlocking the closing of the window with the thread execution. You must make sure that the thread stopped before allowing the window to close. The subject of this answer.

how would he know that he is or he is not owner.

This is something that can be discovered with a winapi call, GetWindowsThreadProcessId(). The Handle property is the fundamental oracle for that. Pretty decent test, but with the obvious flaw that it cannot work when the Handle is no longer valid. Using an oracle in general is unwise, you should always know when code runs on a worker thread. Such code is very fundamentally different from code that runs on the UI thread. It is slow code.

We would like to call it from Thread2

This is not in general possible. Marshaling a call from one thread to a specific other thread requires that other thread to co-operate. It must solve the producer-consumer problem. Take a look at the link, the fundamental solution to that problem is a dispatcher loop. You probably recognize it, that's how the UI thread of a program operates. Which must solve this problem, it gets notifications from arbitrary other threads and UI is never thread-safe. But worker threads in general don't try to solve this problem themselves, unless you write it explicitly, you need a thread-safe Queue and a loop that empties it.

What's if SomeMethod() takes very long time

Not sure I follow, the point of using threads is to let code that takes a long time not do anything to harm the responsiveness of the user interface.

I think it is some kind of general pattern

There is, it doesn't look like this. This kind of code tends to be written when you have an oh-shoot moment and discover that your UI is freezing. Bolting threading on top of code that was never designed to support threading is forever a bad idea. You'll overlook too many nasty little details. Very important to minimize the number of times the worker thread interacts with the UI thread, your code is doing the opposite. Fall in the pit of success with the BackgroundWorker class, its RunWorkerCompleted event gives a good synchronized way to update UI with the result of the background operation. And if you like Tasks then the TaskScheduler.FromCurrentSynchronizationContext() method helps you localize the interactions.

Community
  • 1
  • 1
Hans Passant
  • 922,412
  • 146
  • 1,693
  • 2,536
  • There are many cases where one may not want the closing of the window to affect the thread, but merely cause updates from it to be ignored (e.g. if an application allows windows to be opened to monitor the status of some larger process). It's possible to use a lock and a flag to ensure that one doesn't try to post an event to a window which is being disposed, but I think it's in some ways cleaner to write a `TryInvoke` or `TryBeginInvoke` which returns a failure code rather than throwing an exception if the window can't accept the event. – supercat Jan 27 '15 at 16:11
2
  1. Usually, no. But it could happen if you're using await between the InvokeRequired check and Invoke call without capturing the execution context. Of course, if you're already using await, you're probably not going to be using Invoke and InvokeRequired. EDIT: I just noticed that InvokeRequired will return false when the control handle hasn't been created yet. It shouldn't make much of a difference, because your call will fail anyway when the control hasn't quite been created yet, but it is something to keep in mind.
  2. It doesn't make it thread-safe. It just adds the request to the control's queue, so that it's executed the next available time on the same thread the control was created on. This has more to do with windows architecture than with general thread-safety. The end result, however, is that the code runs on a single thread - of course, this still means you need to handle shared state synchronization manually, if any.
  3. Well, it's complicated. But in the end, it boils down to comparing the thread ID of the thread that created the control, and the current thread ID. Internally, this calls the native method GetWindowThreadProcessId - the operating system keeps track of the controls (and more importantly, their message loops).
  4. Invoke cannot return until the GUI thread returns to its message loop. Invoke itself only posts the command to the queue and waits for it to be processed. But the command is run on the GUI thread, not the Invoke-caller. So the SomeMethod calls in your example will be serialized, and the Invoke call itself will wait until the second call finishes.
  5. This should already be answered. The key point is "only run GUI code on the GUI thread". That's how you get reliable and responsive GUI at all times.
  6. You can use it anywhere you've got a loop or a wait on some queue. It probably isn't all that useful, although I have actually used it already a few times (mostly in legacy code).

However, all of this is just a simple explanation of the workings. The truth is, you shouldn't really need InvokeRequired... well, ever. It's an artifact of a different age. This is really mostly about juggling threads with little order, which isn't exactly a good practice. The uses I've seen are either lazy coding, or hotfixes for legacy code - using this in new code is silly. The argument for using InvokeRequired is usually like "it allows us to handle this business logic safely whether it runs in the GUI thread or not". Hopefully, you can see the problem with that logic :)

Also, it's not free thread-safety. It does introduce delays (especially when the GUI thread is also doing some work that isn't GUI - very likely in code that uses InvokeRequired in the first place). It does not protect you from accesses to the shared state from other threads. It can introduce deadlocks. And don't even get me started on doing anything with code that uses Application.DoEvents.

And of course, it's even less useful once you take await into consideration - writing asynchronous code is vastly easier, and it allows you to make sure the GUI code always runs in the GUI context, and the rest can run wherever you want (if it uses a thread at all).

Luaan
  • 62,244
  • 7
  • 97
  • 116