はじめに
今年は.NET 7でBlazor WebAssemblyでマルチスレッドがサポートされそうということで、このIssueをぼんやりと眺める日々を過ごしていました。
8月に.NET 7ではマルチスレッドに対応しないという内容が投稿され残念に思っていたら、.NET 7 RC2のリリースノートでマルチスレッドに関する内容に言及がありました。
wasm-experimental
ワークロードはWeb Workersを使用したWebAssembly上のマルチスレッドの.NETアプリケーションをサポートするようになりました。これは新しい .NET ランタイム機能です。マルチスレッドのサポートはBlazor WebAssemblyアプリにはまだ統合されていませんが(.NET 8で予定)、実験的なWebAssembly Browser Appテンプレートを使ってプレビュー形式で試すことが可能です。
私は.NET 7 RC2からBlazor WebAssemblyでマルチスレッドを試すことができるのだと勘違いしていたのですが、試すことができるのはwasmbrowser
のテンプレートでした。すみません。
それでも部分的にマルチスレッドを試すことができるのであれば、きっと.NET 8がリリースされてBlazor WebAssemblyにマルチスレッドを導入するときに役立つだろうと思い、動かしてみることにしました。
.NET 7 RC2のリリースノートで紹介されている例を動かしてみる
さきほどの.NET 7 RC2のリリースノートで紹介されている例を手元で動かしてみます。
wasm-experimental
ワークロードをインストールします。
dotnet workload install wasm-experimental
アプリを作成します。
dotnet new wasmbrowser -o sampleapp1
プロジェクトファイルの<PropertyGroup>
に<WasmEnableThreads>
を追加します。
<PropertyGroup>
...
<WasmEnableThreads>true</WasmEnableThreads>
</PropertyGroup>
NuGetでMicrosoft.NET.WebAssembly.Threading
をインストールします。
dotnet add package --prerelease Microsoft.NET.WebAssembly.Threading
program.cs
に.NET 7 RC2のリリースノートのコードを反映させます。テンプレートが作成したJavaScript関係のコードをさわると、他の部分も編集する必要があるので、ここではそのままにしています。
using System;
using System.Threading;
using System.Runtime.Versioning;
using System.Runtime.InteropServices.JavaScript;
[assembly: SupportedOSPlatform("browser")]
new Thread(SecondThread).Start();
Console.WriteLine($"Hello, Browser from the main thread {Thread.CurrentThread.ManagedThreadId}");
static void SecondThread()
{
Console.WriteLine($"Hello from Thread {Thread.CurrentThread.ManagedThreadId}");
for (int i = 0; i < 5; ++i)
{
Console.WriteLine($"Ping {i}");
Thread.Sleep(1000);
}
}
public partial class MyClass
{
[JSExport]
internal static string Greeting()
{
var text = $"Hello, World! Greetings from {GetHRef()}";
Console.WriteLine(text);
return text;
}
[JSImport("window.location.href", "main.js")]
internal static partial string GetHRef();
}
dotnet run
で実行して表示されたURLにアクセスすると、コンソールに以下のように出力されます。
メインスレッドとは別に作成されたスレッドでPing 0
Ping 1
Ping 2
と出力されています。
負荷をかけてみる
ここまでは.NET 7 RC2のリリースノートのコードをそのまま実行しました。マルチスレッドが必要なケースはCPUに負荷がかかるような場合が多いと思うので、そういった例を試してみます。さきほどのprogram.cs
を編集して無限ループで乱数を生成するようにします。
static void SecondThread()
{
Console.WriteLine($"Hello from Thread {Thread.CurrentThread.ManagedThreadId}");
var random = new Random();
while (true)
{
random.Next();
}
}
スレッドが1個のとき
SecondThread
を呼び出した回数だけ乱数を生成するスレッドが作られます。私の8コア16スレッドCPUで1回だけ呼び出した場合は以下のようになりました。タスクマネージャーに表示されたCPUの使用率は10%前後でした。
スレッドが8個のとき
new Thread(SecondThread).Start();
を8個並べて、スレッドが8個のときは以下のようになりました。タスクマネージャーに表示されたCPUの使用率は30%前後でした。
タスクマネージャーの画面下半分のCPUが活躍しているようで、マルチコアが活用されていることがわかります。
CPUのコア数を知るには?
マルチスレッドを使う場合にCPUのコア数を把握したい場合があると思うのですが、通常はSystem.Environment.ProcessorCount
を見ればよいようです。
ただ、今回のコードの中でSystem.Environment.ProcessorCount
の値を確認してみると1
となっていたので、これは何か別の方法がありそうです。
おわりに
Blazor WebAssemblyでマルチスレッドを使うのは.NET 8を待つ必要がありますが、現時点で試せる範囲でマルチスレッドを動かしてみました。マルチコアを活用することも少しだけ体験できましたし、.NET 8を楽しみに待ちたいと思います。