.NET 4.7.2でスレッドの使用量が増加する


解説

同期実行パスで非同期(async)コードを呼び出すと、非同期部分が終了するまで(対応するTaskオブジェクトが完了としてマークされるまで)、スレッドの呼び出しが一時停止されます。これは、C#特有の非同期メソッドの実装により、Taskオブジェクトは継続(continuations)が実行されるまで、完了としてマークされることがないためです。
ほとんどのシナリオでは、継続はスレッド プールのスレッド上で実行されるようにスケジュールされます。従って、スレッドの呼び出しはデフォルトのスレッド プール内のワーカー スレッドの可用性に依存することになります。

このことは主に、同期コンテキスト内で非同期APIを呼び出してジョブを実行させるには、2つのスレッドが必要であることを意味します。アプリケーションには常に空きスレッドが必要であり、それがないと継続タスクの呼び出しとメイン スレッドのブロック解除に使用するワーカー スレッドが足らず、ブロックがエンドレスに発生する可能性があります。
この設計上の制約は、ブロックされたスレッドをうまく活用して操作を実行できるカスタム スレッド スケジューラを使用することで解決できる場合があり、それによって余分なスレッドのニーズをなくすことが可能となります。

Sitecore xConnectはハイパフォーマンスなアプリケーションのベスト プラクティスに基づき、スレッドの使用方法が最適化され、非同期操作をうまく処理することができるよう設計されています。また、過剰なスレッドの消費に対応するためのカスタム スケジューラも定義されています。

しかし残念ながら、.NET Framework 4.7.2 (System.Net.Httpアセンブリ)において、カスタム スレッド スケジューラに関する変更が加えられたことにより、同期-非同期操作によって過剰なスレッドの消費が生じる結果となっています。

解決策

.NET Framework 4.7.2バージョンではなく、以前のバージョンのSystem.Net.Http.dllアセンブリを使用するよう、Sitecoreアプリケーションを構成することが可能です。

C:\Program Files (x86)\Microsoft Visual Studio\[*]\Professional\MSBuild\ Microsoft\Microsoft.NET.Build.Extensions\net471\libフォルダーからSitecoreの\binフォルダーに、System.Net.Http.dllファイルをコピーしてください。アセンブリのバージョンは、4.6.26011.01です。

      - [*]は、適切なVisual Studioのバージョン(2017または2019)に置き換えてください。

新しいアセンブリ バージョンの代わりにGACからのアセンブリを使用するよう、Sitecoreを構成します。

  1. 全ての既存のSystem.Net.Httpのバインディング リダイレクトを、Web.configファイルから削除します。
  2. 以下のバインディング リダイレクトをWeb.configファイルに追加します。
    <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>

System.Net.Httpライブラリの4.2.0.0バージョンが使用されている場合、そこにカスタム スケジューラに対応しているHttpClientクラスの実装が含まれています。従って、xConnectクライアントの同期メソッドが、単一のスレッドだけでリクエストを完了させることができます。