Synchronizing raised events to UI thread possible?
-
I written a ProcessUtility class that allows me to call a process (which in turn runs a commandline executable) asynchronously. The process then raises events indicating completion, progress, etc. When the ProcessUtility class is set up from the UI form as: dim WithEvents P as ProcessUtility_Class P.Start All runs as expected. Events are raised appropriately and, as expected, they are on a different thread from the UI and calls to the UI controls must be invoked to avoid cross-threading errors. I've been reading up on async, synchronization contexts and other threading concepts to better understand the issues. While I can (and have) wrapped every UI call within the events, it seems very redundant to have to do it every time a UI element is utilized. Since the problem isn't really the UI, but the Asynchronous process that is raising events in the "wrong" thread to the UI, it seems that there should be a (better) way to have my ProcessUtility class raise it's events within the threading context of the UI (if desired). That way, only the few events are specialized and whatever happens within them is UI-threadsafe. Perhaps something along the lines of (pseudocode): ProcessUtility_Class Public Event Process_Started(ThisProcess as sender, SecondArg as string) Public sub Start(Form1) Form1.StartInvoke 'Everything from here until endinvoke happens within the MyForm thread RaiseEvent Process_Started(blah, blah) Form1.EndInvoke DoStuffAsynchronously End Sub Is there something like this that can be done that the raised events are always running in the context of the main form that created the ProcessUtility_Class object in the first place? My goal is to avoid having to continuously wrap every UI call with delegates and invokes.
-
I written a ProcessUtility class that allows me to call a process (which in turn runs a commandline executable) asynchronously. The process then raises events indicating completion, progress, etc. When the ProcessUtility class is set up from the UI form as: dim WithEvents P as ProcessUtility_Class P.Start All runs as expected. Events are raised appropriately and, as expected, they are on a different thread from the UI and calls to the UI controls must be invoked to avoid cross-threading errors. I've been reading up on async, synchronization contexts and other threading concepts to better understand the issues. While I can (and have) wrapped every UI call within the events, it seems very redundant to have to do it every time a UI element is utilized. Since the problem isn't really the UI, but the Asynchronous process that is raising events in the "wrong" thread to the UI, it seems that there should be a (better) way to have my ProcessUtility class raise it's events within the threading context of the UI (if desired). That way, only the few events are specialized and whatever happens within them is UI-threadsafe. Perhaps something along the lines of (pseudocode): ProcessUtility_Class Public Event Process_Started(ThisProcess as sender, SecondArg as string) Public sub Start(Form1) Form1.StartInvoke 'Everything from here until endinvoke happens within the MyForm thread RaiseEvent Process_Started(blah, blah) Form1.EndInvoke DoStuffAsynchronously End Sub Is there something like this that can be done that the raised events are always running in the context of the main form that created the ProcessUtility_Class object in the first place? My goal is to avoid having to continuously wrap every UI call with delegates and invokes.
What about using a
BackgroundWorker
for the background task? TheProgressChanged
event will be sent to the correct thread, and you could use theObject userState
parameter for your specific parameters. But in the end, that might create a different overhead for your events and look like just another WTF... -
What about using a
BackgroundWorker
for the background task? TheProgressChanged
event will be sent to the correct thread, and you could use theObject userState
parameter for your specific parameters. But in the end, that might create a different overhead for your events and look like just another WTF...A BackgroundWorker would give similiar function, but I have several more events that BGW supports. And although I probably could inherit a class using BGW, I'd really like to learn the methodology myself and incorporate it into my objects.
-
I written a ProcessUtility class that allows me to call a process (which in turn runs a commandline executable) asynchronously. The process then raises events indicating completion, progress, etc. When the ProcessUtility class is set up from the UI form as: dim WithEvents P as ProcessUtility_Class P.Start All runs as expected. Events are raised appropriately and, as expected, they are on a different thread from the UI and calls to the UI controls must be invoked to avoid cross-threading errors. I've been reading up on async, synchronization contexts and other threading concepts to better understand the issues. While I can (and have) wrapped every UI call within the events, it seems very redundant to have to do it every time a UI element is utilized. Since the problem isn't really the UI, but the Asynchronous process that is raising events in the "wrong" thread to the UI, it seems that there should be a (better) way to have my ProcessUtility class raise it's events within the threading context of the UI (if desired). That way, only the few events are specialized and whatever happens within them is UI-threadsafe. Perhaps something along the lines of (pseudocode): ProcessUtility_Class Public Event Process_Started(ThisProcess as sender, SecondArg as string) Public sub Start(Form1) Form1.StartInvoke 'Everything from here until endinvoke happens within the MyForm thread RaiseEvent Process_Started(blah, blah) Form1.EndInvoke DoStuffAsynchronously End Sub Is there something like this that can be done that the raised events are always running in the context of the main form that created the ProcessUtility_Class object in the first place? My goal is to avoid having to continuously wrap every UI call with delegates and invokes.
smarrocco wrote:
Is there something like this that can be done that the raised events are always running in the context of the main form that created the ProcessUtility_Class object in the first place?
You "could" pass a reference of one of the controls of the main-thread to the worker-thread. That way the control "could" do the invoke. You'd do a myForm.Invoke() from the worker-thread. ..and then you'd be tearing your hair out, trying to fix the "object is already disposed" exceptions; your thread will still be executing when the mainthread starts disposing of stuff. So, I used IlSpy to look at the code from the backgroundworker, and it's using a neat trick; AsyncOperation.Post[^] "Invokes a delegate on the thread or context appropriate for the application model." Will (probably) try it out in the weekend :) --edit There's a how-to here[^].
Bastard Programmer from Hell :suss: If you can't read my code, try converting it here[^]