Catch and Dispatcher.BeginInvoke
-
Multithreading is complicated. It gets even worse when it comes to exceptions. Even a try-catch may fail, like in this gem:
private void ThreadExecuteUpdate() { try { Application.Current?.Dispatcher?.BeginInvoke(new Action(() => { // some code without try-catch })); } catch { } }
Note that the catch is meant to catch everything - and swallow it without any mention anywhere, even no log entry. But that BeginInvoke does make it useless.
Just add a line like throw new Exception("BLAH!"); into the body of the Action and the application crashes. Well, not understanding what he's doing, that guy also failed to do such a simple test.
-
@berniethebernie said in Catch and Dispatcher.BeginInvoke:
But that BeginInvoke does make it useless.
What is this? C#? Can anyone explain why BeginInvoke makes it useless?
-
@boomzilla C#. BeginInvoke takes a function pointer (or anonymous function wrapped in an Action like in this case) and queues it for execution on the application's main dispatch thread.
-
@mott555 And that's...bad? Sorry, I have no idea what a "main dispatch thread" is (I mean...I could guess). I should probably just lie back and think of Oracle or something.
-
@boomzilla ELI5 version: It sends a function to the UI thread to execute there.
-
@masonwheeler Isn't it the other way around, that it sends it somewhere else to free up the UI thread?
-
@boomzilla It's bad because whoever wrote it tried to put in in a try...catch and completely failed because you can't catch an exception from a function that is being executed at some other time and maybe even on another thread.
EDIT: Main dispatch thread is the UI thread. Whatever thread is doing the whole Win32 message pump thing.
-
@hungrier said in Catch and Dispatcher.BeginInvoke:
@masonwheeler Isn't it the other way around, that it sends it somewhere else to free up the UI thread?
No. That's
Task.Run
. AnInvoke
is useful for reporting back to the UI. For example, if you're performing a long-running task in a background thread, to keep from blocking the main thread, and you want to update a progress bar (which has thread affinity with the UI thread) you would do that with anInvoke
.
-
@boomzilla said in Catch and Dispatcher.BeginInvoke:
And that's...bad? Sorry, I have no idea what a "main dispatch thread" is
The BeginInvoke returns immediately with no error, as it successfully queued the action. Any exception thrown within the action needs to be caught in the action as it's in its own callstack
-
@hungrier said in Catch and Dispatcher.BeginInvoke:
@masonwheeler Isn't it the other way around, that it sends it somewhere else to free up the UI thread?
No, you're thinking of the Tasks library. This code is old .Net 1.x/2.x stuff.
BeginInvoke says "sit on this function call until UI thread does it's normal Windows messagepump stuff at some point in the near future, and run it then".
You can't catch exceptions that way, because you're just queuing up the function to run, you're not actually running that function at that time. So the catch will never catch anything, and actual exceptions thrown by the function will be uncaught.
EDIT: I guess like 3 people explained it at the same time, but whatever.
EDIT EDIT: I should also mention, I can't speak for WPF, but if you're writing WinForms code and even touching BeginInvoke you're probably doing something wrong. You should be using the BackgroundWorker class which abstracts most of this shit away.