I am only somewhat familiar with multi-threading in that I\'ve read about it but have never used it in practice.
I have a project that uses a third party library tha
If you are using WPF:
You need a reference to the Dispatcher object which manages the UI thread. Then you can use the Invoke or BeginInvoke method on the dispatcher object to schedule an operation which takes place in the UI thread.
The simplest way to get the dispatcher is using Application.Current.Dispatcher. This is the dispatcher responsible for the main (and probably the only) UI thread.
Putting it all together:
class MyClass
{
// Can be called on any thread
public ReceiveLibraryEvent(RoutedEventArgs e)
{
if (Application.Current.CheckAccess())
{
this.ReceiveLibraryEventInternal(e);
}
else
{
Application.Current.Dispatcher.Invoke(
new Action<RoutedEventArgs>(this.ReceiveLibraryEventInternal));
}
}
// Must be called on the UI thread
private ReceiveLibraryEventInternal(RoutedEventArgs e)
{
// Handle event
}
}
You don't need the specific control, any control (including the Form) will do. So you could abstract it away from the UI somewhat.
I just ran into the same situation. However, in my case I couldn't use SynchronizationContext.Current, because I did not have access to any UI components and no callback to capture the current synchronization context. It turns out that if the code is not currently running in a Windows Forms messge pump, SynchronizationContext.Current will be set to a standard SynchronizationContext, which will just run Send calls on the current thread and Post calls on the ThreadPool.
I found this answer explaining the different types of synchronization contexts. In my case the solution was to create a new WindowsFormsSynchronizationContext object on the thread that would later start the message pump using Application.Run(). This synchronization context can then be used by other threads to run code on UI thread, without ever touching any UI components.
Is there a way around this?
Yes, the work-around would be for you to create a thread-safe queue.
In either case, because your queue is being written by two different threads (the 3rd-party thread is enqueueing, and your thread is dequeueing), it needs to be a thread-safe, protected queue.
Use SynchronizationContext.Current, which will point to something that you can synchronize with.
This will do the right thing™ depending on the type of application. For a WinForms application, it will run this on the main UI thread.
Specifically, use the SynchronizationContext.Send method, like this:
SynchronizationContext context =
SynchronizationContext.Current ?? new SynchronizationContext();
context.Send(s =>
{
// your code here
}, null);
The handling method could simply store the data into a member variable of the class. The only issue with cross-threading occurs when you want to update threads to controls not created in that thread context. So, your generic class could listen to the event, and then invoke the actual control you want to update with a delegate function.
Again, only the UI controls that you want to update need to be invoked to make them thread safe. A while ago I wrote a blog entry on a "Simple Solution to Illegal Cross-thread Calls in C#"
The post goes into more detail, but the crux of a very simple (but limited) approach is by using an anonymous delegate function on the UI control you want to update:
if (label1.InvokeRequired) {
label1.Invoke(
new ThreadStart(delegate {
label1.Text = "some text changed from some thread";
}));
} else {
label1.Text = "some text changed from the form's thread";
}
I hope this helps. The InvokeRequired is technically optional, but Invoking controls is quite costly, so that check ensure it doesn't update label1.Text through the invoke if its not needed.