Events, Callbacks, and Thread Safety in Measurement Studio .NET Class Libraries

NI-VisaNS .NET Class Library Help for Visual Studio 2010

Edition Date: August 2012

Part Number: 370627F-01

»View Product Info
Download Help (Windows Only)


When programming with Measurement Studio class libraries, such as NI-DAQmx, NI-488.2, NI-VISA, and Analysis, you often install callbacks with the class libraries so that those libraries can execute parts of your code. Some common examples of using callbacks with Measurement Studio include passing a delegate when calling an asynchronous method or installing an event handler.

When working with callbacks, it is important to know which thread callbacks are executed on. Knowing this information is important because some operations are only allowed on certain threads. For example, you may access Windows Forms controls only from the user interface (UI) thread that created them. Accessing Windows Forms controls from other threads results in undefined behavior. The Visual Studio debugger can detect this scenario and reports the following: Cross-thread operation not valid: Control '...' accessed from a thread other than the thread it was created on.

Refer to the following sections for more information:

Thread Safe Events and Callbacks in the .NET Framework
This section describes how to specify which thread is used to execute your event handlers and callbacks in the .NET Framework.
Customizing Event and Callback Behavior in the .NET Framework
This section describes how to customize the synchronization behavior used to execute your event handlers and callbacks in the .NET Framework.
Resolving Compiler Warnings from SynchronizingObject in the .NET Framework
This section describes how to update existing Microsoft .NET Framework 1.1 code to Microsoft .NET Framework 2.0 and later.

Thread Safe Events and Callbacks in the .NET Framework

.NET Framework 2.0 introduced the SynchronizationContext class, which governs how callbacks execute for a particular thread or context. Each thread in .NET Framework 2.0 and later has one instance of SynchronizationContext. By default, SynchronizationContext provides a free-threaded context with no synchronization. However, a thread may customize the behavior of its SynchronizationContext. For example, for a UI thread, the SynchronizationContext always executes callbacks on the UI thread.

Measurement Studio class libraries use an instance of the SynchronizationContext class to execute callbacks. Which instance of SynchronizationContext they use is determined in one of the following three ways:

  1. Event-based Asynchronous Pattern—If you are installing an event handler with an event that follows the event-based asynchronous pattern, then the SynchronizationContext of the thread you use to call the asynchronous method executes your event handler. Asynchronous methods that follow the event-based asynchronous pattern end in Async and have corresponding events that end in Completed.

    For example, since the SynchronizationContext of a UI thread executes callbacks in the UI thread, calling an asynchronous method from the UI thread causes the corresponding event to be raised in that same thread.

    VB.NET
    Private Sub Button1_Click(ByVal sender As Object, ByVal e As EventArgs) _
            Handles Button1.Click
        ' Start an asynchronous DAQ write using the
        ' event-based asynchronous pattern.
        _myDaqComponent.WriteAsync(_generatedData)
    End Sub
    
    ' The Form's InitializeComponent method installs this event handler.
    Private Sub _myDaqComponent_WriteCompleted(ByVal sender As Object, ByVal e _
            As WriteCompletedEventArgs) Handles _myDaqComponent.WriteCompleted
        ' Because the UI thread calls WriteAsync,
        ' this handler will also execute in the UI thread.
    End Sub
    C#
    private void button1_Click(object sender, EventArgs e)
    {
        // Start an asynchronous DAQ write using the
        // event-based asynchronous pattern.
        _myDaqComponent.WriteAsync(_generatedData);
    }
    
    // The Form's InitializeComponent method installs this event handler.
    private void _myDaqComponent_WriteCompleted(object sender, WriteCompletedEventArgs e)
    {
        // Because the UI thread calls WriteAsync,
        // this handler will also execute in the UI thread.
    }
  2. Events—For all events that are not part of the event-based asynchronous pattern, the SynchronizationContext of the thread you use to install the event handler executes your event handler.

    For example, since the SynchronizationContext of a UI thread executes callbacks in the UI thread, installing an event handler from the UI thread causes your event handler to execute in that same thread.

    VB.NET
    Private Sub Button1_Click(ByVal sender As Object, ByVal e As EventArgs) _
            Handles Button1.Click
        ' Install an event handler for DAQ warnings
        AddHandler DaqSystem.Local.DaqWarning, AddressOf DaqWarning
    End Sub
    
    Private Sub DaqWarning(ByVal sender As Object, ByVal e As DaqWarningEventArgs)
        ' Because the UI thread installs this handler,
        ' it will execute in the UI thread.
    End Sub
    C#
    private void button1_Click(object sender, EventArgs e)
    {
        // Install an event handler for DAQ warnings
        DaqSystem.Local.DaqWarning += Local_DaqWarning;
    }
    
    void Local_DaqWarning(object sender, DaqWarningEventArgs e)
    {
        // Because the UI thread installs this handler,
        // it will execute in the UI thread.
    }
  3. Passing Delegates to Aysnchronous Methods—For cases where you pass delegates to methods in Measurement Studio class libraries, the SynchronizationContext of the thread you use to call the method executes your delegate.

    For example, since the SynchronizationContext of a UI thread executes callbacks in the UI thread, passing a delegate to a method from the UI thread causes your delegate to execute in that same thread.

    VB.NET
    Private Sub Button1_Click(ByVal sender As Object, ByVal e As EventArgs) _
            Handles Button1.Click
        ' Start an asynchronous DAQ read
        _reader.BeginReadMultiSample(_numberOfSamples, AddressOf ReadComplete, Nothing)
    End Sub
    
    Private Sub ReadComplete(ByVal result As IAsyncResult)
        ' Because the UI thread calls BeginReadMultiSample,
        ' this callback will execute in the UI thread.
    End Sub
    C#
    private void button1_Click(object sender, EventArgs e)
    {
        // Start an asynchronous DAQ read
        _reader.BeginReadMultiSample(_numberOfSamples, ReadComplete, null);
    }
    
    private void ReadComplete(IAsyncResult result)
    {
        // Because the UI thread calls BeginReadMultiSample,
        // this callback will execute in the UI thread.
    }

Customizing Event and Callback Behavior in the .NET Framework

Measurement Studio classes that use SynchronizationContext implement the ISupportSynchronizationContext interface, which exposes a Boolean property named SynchronizeCallbacks. This property determines whether the class uses SynchronizationContext in the manner described in the previous section, or simply executes your callback directly, with no thread synchronization.

The default value for SynchronizeCallbacks is true, which indicates that callbacks execute using SynchronizationContext in the manner described in the previous section.

You might prefer Measurement Studio class libraries to execute your callbacks directly from the library thread performing the operation rather than using SynchronizationContext. Executing callbacks directly is faster than executing them using SynchronizationContext because SynchronizationContext switches threads. If you do not want your callbacks to execute using SynchronizationContext, set SynchronizeCallbacks to false.

Note: Since Windows Forms controls can only be accessed from the UI thread, you cannot access Windows Forms controls in your callback if you set SynchronizeCallbacks to false. Accessing Windows Forms controls from threads other than the UI thread results in undefined behavior. In .NET Framework 2.0 and later, some Windows Forms controls throw exceptions when accessed from other threads.

Resolving Compiler Warnings from SynchronizingObject in the .NET Framework

Measurement Studio class libraries that support the .NET Framework 1.1 use the SynchronizingObject property and the ISynchronizeInvoke interface to indicate how callbacks are executed. For Measurement Studio class libraries that support the .NET Framework 2.0 and later, this mechanism has been replaced with the new mechanism described in the previous Thread Safe Events and Callbacks in Visual Studio section.

SynchronizingObject has been obsoleted in Measurement Studio class libraries for .NET Framework 2.0 and later. Accessing this property produces compiler warnings. If you were using SynchronizingObject in .NET Framework 1.1, you must update your code to use SynchronizationContext in .NET Framework 2.0 and later. For more information about SynchronizationContext, refer to Thread Safe Events and Callbacks in Visual Studio. The difference between these two mechanisms is summarized in the following table.

Desired Effect Using .NET Framework 1.1 Using .NET Framework 2.0 and Later
Execute callbacks in a Windows Forms UI thread. Set SynchronizingObject to your Windows Form.
  • Install event handlers and start asynchronous operations from your UI thread, and ensure that SynchronizeCallbacks is set to true, which is the default.
Execute callbacks in any thread (free-threaded). Set SynchronizingObject to a null reference (Nothing in Visual Basic), which is the default.
  • Do not install event handlers or start asynchronous operations from a UI thread. Use the ThreadPool class or create a separate worker thread to handle events or perform asynchronous operations.
  • Alternatively, set SynchronizeCallbacks to false. Setting this property on an object affects all the callbacks executed by that object.

WAS THIS ARTICLE HELPFUL?

Not Helpful