Friday, October 4, 2024
HomeC#What’s the SynchronizationContext used for? – csharp.christiannagel.com

What’s the SynchronizationContext used for? – csharp.christiannagel.com


Creating Home windows purposes, UI controls are certain to the UI thread. .NET made it to completely different iterations with completely different patterns coping with asynchronous programming. .NET 4.0 launched the Job Parallel Library (TPL) and C# 5 added the async and await key phrases. Along with these enhancements of .NET, and the synchronization context, invoking strategies that make use of various threads has changing into quite a bit simpler.

Just lately a reader of my e book Skilled C# and .NET – 2021 Version has requested me for extra info on the synchronization context has requested me for extra info on the synchronization context. My e book already has quite a bit info on duties, threads, async, and await – however let’s give it one other spin to demostrate some great benefits of the synchronization context.

Railroad track switch

Synchronization Context and Console Purposes

Earlier than utilizing a Home windows utility, let’s begin with a console utility creating a brand new job, and utilizing the async/await key phrases.

First, the strategy ShowThreadAndTask shows the present thread id and job id. The id of the present thread could be accessed utilizing Setting.CurrentManagedThreadId, the id of the duty with Job.CurrentId. In all probability you additionally know the older API to entry the thread-id: Thread.CurrentThread.ManagedThreadId.

Show Thread and Task Ids

With the top-level statements of a console utility, first the thread and job ids are displayed, subsequent the asynchronous technique SampleAsync is invoked that makes use of Job.Run to create a brand new job. With the await key phrase, the execution of the top-level statements don’t proceed till the duty completes. Lastly, the thread and job ids are displayed once more.

Creating a Task and calling an async method

Wanting on the ensuing output, you’ll be able to see that the top-level statements begin with a distinct thread than they finish with. With the next screenshot, the primary thread used has thread-id 1, after calling the SampleAsync technique, the thread-id is 4. You may get a distinct end result with each run of the applying. The highest-level statements don’t present a job identifier. Inside the Job.Run technique, the thread-id is 4, and there’s a task-id 1. When the duty completes, the identical thread is used after the async key phrase to run the remaining a part of the top-level statements. As a result of the duty accomplished, the identical thread now not is related to a job.

Running the application

This is a vital level to know. Earlier than and after the async technique is known as, the thread can differ. With console purposes there’s no synchronization context, so the thread can change earlier than and after the async technique is known as.

Invoking the ConfigureAwait technique you’ll be able to configure the conduct of the await to make use of a synchronization context and thus to proceed on the identical thread after the await. (ConfigureAwait(continueOnCapturedContext: true)). The default setting is true, to proceed on the identical thread. Nonetheless, as a result of a console utility doesn’t have the synchronization context set, a context isn’t captured, and thus the continuation can proceed on a distinct thread with the default setting. When there’s no captuered context, altering the setting of ConfigureAwait doesn’t change the conduct.

A console utility doesn’t have a synchronization context set as could be verified utilizing SynchronizationContext.Present as proven within the following code snippet:

Checking the synchronization context

Blocking the present thread

Let’s proceed with a Home windows utility. I’m utilizing WinUI for this pattern. You’ll see comparable expertise with different UI applied sciences. The applying implements a easy calculator the place the person can add and subtract two values. The primary model of the Calculator specifies the BlockingAdd technique which sleeps for a while earlier than returning a end result utilizing Thread.Sleep. This technique is a blocking name. Calling this from the UI thread, the UI is frozen till the strategy completes. That’s why it’s best to by no means do that in a Home windows utility – don’t name blocking strategies from the UI thread. If you happen to attempt to run the applying clicking the Blocking Add button, the entire utility isn’t responding till the calculation is accomplished. You even can’t transfer the window. The UI thread is blocked.

BlockingAdd

If you should invoke such a blocking technique, you’ll be able to create a customized job as proven within the following code snippet with the Job class:

![Creating a custom task]https://csharpdotchristiannageldotcom.information.wordpress.com/2022/08/winui-customtask.png)

Operating the applying you’ll be able to see that the UI is responsive this time. Nonetheless, the end result returned can’t be immediately assigned to the textbox. If this may be finished (you’ll be able to attempt to do away with the DispatcherQueue used within the technique CustomTask and immediately assign the end result to the textbox), and the applying would throw an exception. With WinUI it’s a COMException with the error RPC_E_WRONG_THREAD and the message The applying known as an interface that was marshalled for a distinct thread..

To invoke strategies or properties of UI parts, a change to the UI thread is required. With WinUI, this may be finished utilizing the DispatcherQueue property of the DependencyObject class. The DependencyObject class is a base class of the WinUI parts. The DispatcherQueue property property returns a DispatcherQueue object that gives strategies to get again into the UI thread. You may move a DispatcherQueueHandler delegate to the TryEnqueue technique. The tactic referenced from the delegate is then known as from the UI thread. Behind the scenes that is finished passing info to the message queue of the UI thread, so the UI thread can execute this technique as quickly the thread is on the market to do that. Relying on how briskly this could occur, a precedence could be specified with the DispatcherQueuePriority parameter. Strategies with a better precedence are executed earlier than strategies with a decrease precedence. The default precedence is DispatcherQueuePriority.Regular.

You may create a timer with DispatcherQueue.CreateTimer to execute a technique on the UI thread primarily based on a time interval.

Do we have to change to the UI thread?

To test if a change to the UI thread is required, the DispatcherQueue class presents the strategy HasThreadAccess. This technique returns true if the present thread is the UI thread. The implementation within the following code snippet writes a string to a ListView management. If the strategy is known as from the UI thread, a change isn’t required. With a distinct thread, the thread change is finished earlier than writing the string to the ListView.

Check if a thread switch is required

You may argue that you just don’t immediately write to UI parts from the code-behind, and as a substitute use a view-model. The MVVM sample is an efficient follow and in addition defined within the e book intimately in Chapter 30, Patterns with XAML Apps. Nonetheless, view-models don’t assist you on this situation. Setting properties which might be certain to UI parts must be finished from the UI thread, so you have got the identical drawback.

Utilizing WPF, there’s a option to fill an ObservableCollection from a background thread that’s certain to a UI factor for those who synchronize the entry. To specify the lock object for synchronizaton, you should use BindingOperations.EnableCollectionSynchronization.

Utilizing Async/Await with Home windows Purposes

Checking if the code is operating within the UI thread and switching to the UI thread in case it isn’t, requires some work. The synchronization context and the async/await key phrases makes this job straightforward.

Let’s change the implementation of the Add technique of the Calculator class to make use of the async/await key phrases. The implementation proven within the following code snippet doesn’t block the calling thread. As a substitute of utilizing Thread.Sleep, the strategy Job.Delay returns a Job that may be awaited. With the console utility you’ve seen {that a} completely different thread can run earlier than and after the await. That is now completely different with the Home windows utility. In any case, the calling thread isn’t blocked. With the AddAsync technique, if the UI thread invokes this technique, after the delay is full, due to the synchronization context, the UI thread will proceed to return the end result. The SubtractAsync technique is carried out otherwise. Utilizing the ConfigureAwait technique, continueOnCapturedContext is ready to false. The SubtractAsync technique doesn’t use the synchronization context, and after the await completes, a distinct thread can be utilized to run the remaining statements. In case the synchronization context isn’t wanted with the code after the await, you’ll be able to cut back the load of the UI thread and preserve a distinct thread at work. That is the case right here – with the SubtractAsync technique, the subtraction could be finished from a distinct thread. Nonetheless, as this calculation is actually quick, it shouldn’t actually matter.

Async calculator

The next code snippet reveals the occasion handler technique implementation clicking the Add and Subtract buttons. The await key phrase is used when calling the AddAsync and SubtractAsync strategies. As a result of this occasion handler is began from the UI thread, and ConfigureAwait isn’t used to alter the await conduct, the UI thread is used once more after the await. That’s why the end result could be immediately assigned to the Textual content property of the TextBox management with out doing a thread change.

Invoking the Async Calculator

Synchronization Context

Utilizing WinUI 3, SynchronizationContext.Present returns a DispatcherQueueSynchronizationContext object. With WPF, UWP, Home windows Varieties, and .NET MAUI you get different implementations of the concrete SynchronizationContext class.

Remember on an necessary concern. In case you create a brand new job earlier than invoking an asynchronous technique, and wish the UI thread afterward: if a thread completely different from the UI thread is used with the await, this job doesn’t have the synchronization context to seize. Thus, after the await the change to the UI thread can not occur. So there are eventualities the place switching to the UI thread manually remains to be required in some instances.

Take away

UI parts are certain to the UI thread. If you should entry UI parts from a distinct thread, you should change to the UI thread. The work wanted to do that could be simplified with the async/await key phrases, and the assistance of the synchronization context. It’s nonetheless good to bear in mind what occurs behind the scenes, so in particular circuimstances you’ll be able to perceive the explanations when not every little thing works as anticipated.

Take pleasure in studying and programming!

Christian

You may assist my weblog by shopping for a espresso. Throughout these months all of the coffees (and extra) might be used to assist the Ukraine.

RELATED ARTICLES

LEAVE A REPLY

Please enter your comment!
Please enter your name here

Most Popular

Recent Comments