Higher thread usage in .NET 4.7.2


Description

Calling an async code in a synchronous execution path leads to the calling thread being paused until the async part finishes (the corresponding Task object is marked as completed). Because of the specific implementation of async methods in C#, the Task object cannot be marked as complete until continuations are executed. In most of the scenarios, continuations are scheduled to be executed on thread pool threads. Therefore, the calling thread starts to depend on the availability of worker threads in the default thread pool.

This mostly means that calling an async API in a synchronous context needs two threads to do the job. An application must always have free threads, otherwise an endless blocking can occur due to a lack of worker threads to call continuation and unblock main thread. This design constraint could be resolved by using a custom thread scheduler that can take advantage of a blocked thread to execute the operation, thereby eliminating the need for an extra thread.

Sitecore xConnect is written according to high-performance application best practices, with optimized thread consumption. Its design allows for taking full advantage of async operation processing and it defines the custom scheduler to tackle excessive thread consumption.

Unfortunately, the recent changes in .NET Framework 4.7.2 (System.Net.Http assembly) removed the respect of custom thread schedulers. This has led to excessive thread consumption by sync-async operations.

Solution

It is possible to configure a Sitecore application to use a previous version of the System.Net.Http.dll assembly instead of the .NET Framework 4.7.2 version.

Copy the System.Net.Http.dll file from the C:\Program Files (x86)\Microsoft Visual Studio\[*]\Professional\MSBuild\ Microsoft\Microsoft.NET.Build.Extensions\net471\lib folder to the \bin folder of Sitecore. Assembly file version 4.6.26011.01.

      - where [*] should be replaced with relevant Visual Studio version (2017 or 2019).

Configure Sitecore to use the new assembly version instead of the assembly from GAC:

  1. Remove all existing binding redirects for System.Net.Http from the Web.config file.
  2. Add the following binding redirect to the Web.config file:
    <dependentAssembly>
      <assemblyIdentity name="System.Net.Http" publicKeyToken="b03f5f7f11d50a3a" xmlns="urn:schemas-microsoft-com:asm.v1" />
      <bindingRedirect oldVersion="0.0.0.0-4.2.0.0" newVersion="4.2.0.0" xmlns="urn:schemas-microsoft-com:asm.v1" />
    </dependentAssembly>

When version 4.2.0.0 of System.Net.Http library is used, it contains an implementation of the HttpClient class that respects custom schedulers. Therefore, synchronous methods of xConnect client require just a single thread to complete a request.