Добавил:
Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:
vermeir_nico_introducing_net_6_getting_started_with_blazor_m.pdf
Скачиваний:
19
Добавлен:
26.06.2023
Размер:
11.64 Mб
Скачать

Chapter 11 Advanced .NET 6

threads for I/O work. There are not that many cases where we want to use completion ports. The usage of these types of threads usually happens in the .NET libraries themselves; the parts that handle I/O interrupt and request.

Most of the time, it is better to use worker threads from the threadpool, but there are a few scenarios where it might be useful to create your own threads.

•\

Change priority of a thread.

•\

Create a foreground thread.

•\

Work that takes a long time.

Threadpool threads cannot be made into foreground threads, so if that is what you need, for example, to keep the process open when the main thread exits, then you need to create your own thread. Same with thread prioritization that also requires a new thread. Long running tasks can be scheduled on threadpool threads, but the number of threads there is limited before the system starts creating new ones, which is something we want to avoid if possible because of performance reasons. So if we schedule a lot of long running tasks, we might run out of threadpool threads; for that, we might switch to creating our own threads. As always this is very dependent on your situation, so handle with care.

Async in .NET 6

Async/Await has been around for a while now in .NET, and most .NET developers should be familiar with how to use it. .NET 6 comes with a few new additions to the await/async pattern, but let’s explore the basics before we dive into the new stuff.

Await/Async

Listing 11-6.  Async operation

public async Task<string> FetchData()

{

var client = new HttpClient();

HttpResponseMessage response = await client.GetAsync("https://www. apress.com").ConfigureAwait(false);

304

Chapter 11 Advanced .NET 6

string html = await response.Content.ReadAsStringAsync(). ConfigureAwait(false);

return html;

}

As an example, Listing 11-6 shows a simple method that uses HttpClient to fetch a web endpoint. We notice a couple of different things; first we have marked the method as async; we can only use the await keyword in a method, lambda or anonymous method that is modified with the async keyword. Await/async does not work in synchronous functions, in unsafe contexts, or in lock statement blocks. The return type of the method is Task<string>. Task is one of the go-to return types of asynchronous methods. Task comes from the Task Parallel Library in .NET and symbolizes a unit of work and its status; whenever we await a Task we wait for its status to become complete before continuing executing the rest of the method. When we await GetAsync, for example, the method execution stops there, scheduling the rest of the method as a continuation. Once the HTTP call completes, the result is passed into the continuation and the rest of the method executes. If we decompile this using a decompiler like ILSpy, we can clearly see how the framework is introducing statemachines into our code to keep track of the state of Tasks.

Listing 11-7.  State machines

.class nested private auto ansi sealed beforefieldinit '<FetchData>d__0' extends [System.Runtime]System.Object

implements [System.Runtime]System.Runtime.CompilerServices. IAsyncStateMachine

{

.override method instance void [System.Runtime]System.Runtime. CompilerServices.IAsyncStateMachine::SetStateMachine(class [System. Runtime]System.Runtime.CompilerServices.IAsyncStateMachine)

Listing 11-8 shows more intermediate language; this is the part where the HTTP calls and the reading of the data happens.

305

Chapter 11 Advanced .NET 6

Listing 11-8.  Async calls in IL

// num = (<>1__state = 0); IL_004d: ldarg.0

IL_004e: ldc.i4.0 IL_004f: dup IL_0050: stloc.0

IL_0051: stfld int32 Foo/'<FetchData>d__0'::'<>1__state'

//<>u__1 = awaiter2; IL_0056: ldarg.0 IL_0057: ldloc.2

IL_0058: stfld valuetype [System.Runtime]System.Runtime.CompilerServices. ConfiguredTaskAwaitable`1/ConfiguredTaskAwaiter<class [System.Net. Http]System.Net.Http.HttpResponseMessage> Foo/'<FetchData>d__0'::'<>u__1'

//<FetchData>d__0 stateMachine = this;

IL_005d: ldarg.0

IL_005e: stloc.s 4

//<>t__builder.AwaitUnsafeOnCompleted(ref awaiter2, ref stateMachine); IL_0060: ldarg.0

IL_0061: ldflda valuetype [System.Runtime]System.Runtime.CompilerServices. AsyncTaskMethodBuilder`1<string> Foo/'<FetchData>d__0'::'<>t__builder' IL_0066: ldloca.s 2

IL_0068: ldloca.s 4

IL_006a: call instance void valuetype [System.Runtime]System.Runtime. CompilerServices.AsyncTaskMethodBuilder`1<string>::AwaitUnsafeOnCo mpleted<valuetype [System.Runtime]System.Runtime.CompilerServices. ConfiguredTaskAwaitable`1/ConfiguredTaskAwaiter<class [System. Net.Http]System.Net.Http.HttpResponseMessage>, class Foo/'<FetchData>d__0'>(!!0&, !!1&)

//return;

IL_006f: nop

IL_0070: leave IL_01a3

As you can tell, a lot of code is generated when using await/async. That is why I want to advise you to use this carefully; async is not always better or faster than synchronous development.

306

Chapter 11 Advanced .NET 6

The Task object generated when awaiting an action captures the context it was called from. When you await an async method, and don’t specify ConfigureAwait(false), the method will do its work on the thread pool and switch back to the caller’s context when finished. This is exactly the behavior that you want when you request a webresult and immediately put the data into a property that is bound against, since binding happens on the UI thread. But this is not what we want when we’re executing code in a library or in a service class, so that’s where we’ll use ConfigureAwait(false).

Figure 11-6.  Captured context in a Task

In ASP.NET, ever since .NET Core 3.1, we do not need to call ConfigureAwait(false) because there is no SynchronizationContext to return to. Blazor on the other hand does have a SynchronizationContext.

Listing 11-9 shows an example of where to use ConfigureAwait(false) and where not to. The example is done in a WinForms application.

Listing 11-9.  Usage of ConfigureAwait(false)

private async Task FetchData()

{

var service = new ResourcesService();

var result = await service.FetchAllResources();

//textblock is bound against Json JsonTextbox.Text = result

}

307