Professional OPC
Development Tools

logos

Callbacks from subscriptions, on which thread?

More
30 Oct 2014 11:39 #2474 by Emilio
Thanks for the very detailed answer.

Actually I'm not in a GUI, rather inside a Windows Service that receives already different calls from different threads, including a WCF Service with "per call" behaviour. Therefore I had already one internal message queue with synchronized access.

I've just put the message queue between OPC callbacks and business logic and the problem was automatically solved.

Next time I will read first the documentation more carefully :-)

Thanks,
Emilio

Please Log in or Create an account to join the conversation.

More
30 Oct 2014 10:18 #2473 by support
Thanks for the questions. I will start with the quotation from the Concepts document:

12.15 Multithreading and Synchronization
The EasyDAClient, EasyAEClient and EasyUAClient objects and all their related helper objects are thread-safe.
In QuickOPC.NET and QuickOPC-UA, if you are hooking to the event notifications provided by the EasyDAClient , EasyAEClient or EasyUAClient component, or processing these notifications in your callback methods, make sure that you understand how the component generates these events and what threading issues it may involve. This is how it works:
The EasyDAClient, EasyAEClient or EasyUAClient object has a SynchronizationContext property. This property can either be set to a null reference, or to an instance of SystemThreading.SynchronizationContext class. When the SynchronizationContext is set to a null reference, EasyDAClient, EasyAEClient or EasyUAClient calls any event handlers or callback methods on its own internal thread, which is possibly different from any threads that you have in your application. When the SynchronizationContext is set to a concrete instance, the synchronization model implemented by this object is used. The EasyDAClient, EasyAEClient or EasyUAClient then typically uses the Post method of this synchronization context to invoke event handlers in your application.
When the EasyDAClient, EasyAEClient or EasyUAClient object is initially created, it attempts to obtain the synchronization context from the current thread (the thread that is executing the constructor). As a result, if the thread constructing the EasyDAClient, EasyAEClient or EasyUAClient object has a synchronization context associated with it, it will become the value of the SynchronizationContext property. Otherwise, the SynchronizationContext property will be set to a null reference. This way, the synchronization context propagates from the constructing thread.
Access to Windows Forms controls is not inherently thread safe. If you have two or more threads manipulating the state of a control, it is possible to force the control into an inconsistent state. Other thread-related bugs are also possible, such as race conditions and deadlocks. It is important to make sure that access to your controls is performed in a thread-safe way. Thanks to the mechanism described above, this is done automatically for you, provided that the constructor of the EasyDAClient, EasyAEClient or EasyUAClient object is called on the form’s main thread, as is the case if you place the EasyDAClient , EasyAEClient or EasyUAClient component on the form’s design surface in Visual Studio. This works because by default, Windows Forms sets the synchronization context of the form’s main thread to a properly initialized instance of System.Windows.Forms.WindowsFormsSynchronizationContext object.
Similarly, Windows Presentation Foundation (WPF) applications use System.Windows.Threading.DispatcherSynchronizationContext to achieve the same thing.
If your application is not based on the above frameworks, or is using them in an unusual way, you may have to take care of the synchronization issues related to event notification yourself, either by directly coding the synchronization mechanism, or by implementing and using a class derived from SystemThreading.SynchronizationContext.
In QuickOPC.NET and QuickOPC-UA, objects in the Forms namespaces follow the general Windows Forms rules and conventions, meaning that any public static (Shared in Visual Basic) type members are thread-safe. Any instance members are not guaranteed to be thread safe.
[...]


In general, your question is about the guarantees we give you regarding the thread identity and timing (synchronization) of the event handler calls. The short answer is a) when the SynchronizationContext is not null, the guarantees are given by the SynchronizationContext, and b) when the SynchronizationContext is null, we give you no guarantees: the event handlers can be called on any thread and at any time.

In this sense, it is irrelevant HOW we actually do it; your code should follow the guarantees that are given (or are *not* given), because the presence or absence of guarantees will stay constant in any foreseeable future version, while the exact implementation may change.

I think that the current implementation has an unmanaged thread at its core; the thread pulls messages from an internal queue and dispatches them to the event receivers. In this process, it switches to a managed thread. Even if we keep using the same unmanaged thread, it may be that the switch to a managed thread may use a different thread each time. I have not actually looked into it and verified this, because it is not relevant - we do not give the guarantee. If, however, you are making e.g. a Windows Forms app and the SynchronizationContext is set properly (which happens automatically when the component is created on the main UI thread), then your event handlers should be called always on the same thread - and it should be the main UI thread, with message pump etc., and the ability to directly access UI controls.

Best regards

Please Log in or Create an account to join the conversation.

More
30 Oct 2014 06:08 #2472 by Emilio
The issue:
Some time ago, when I started the project with the OpcLabs library, one of the very first thing I checked was whether the data changes from subscriptions come via other threads, different than the one that creates the EasyDAClient object.

I noticed that the thread was the same (same ManagedThreadID, which is possible to achieve by using Dispatchers) therefore I didn't synchronize upon receiving the data change.

The project grew and, when we have more items under subscription, sudden crashes came always more frequently.
I've just checked right now, that the callback from a subscription actually comes from different threads....

For me it is clear: I haven't synchronized, so I have a crash. Specially during handling Collections, where the collection changes at the same time that another thread is trying to add/delete items.
It is not a big problem, I just have to synchronize.

My question to you (it is just a curiosity):

Sometimes the callback comes within the SAME thread that created the EasyDAClient, sometimes NOT.
How is it possible?

I'm expecting that, either the callback is synchronized, or not.
It should always be the same thread or always not.

I change the question a little bit:
How do you call the callback from subscription?

Thanks in advance,
Emilio

Please Log in or Create an account to join the conversation.

Moderators: support
Time to create page: 0.164 seconds

      

 Recommend this on Google