-
Notifications
You must be signed in to change notification settings - Fork 5.3k
Description
Description
ComponentBase.InvokeAsync does not call on the UI thread when called from another thread.
Reproduction Steps
Test project repo: Threads Test
The project msut have <WasmEnableThreads>true</WasmEnableThreads> and reference Microsoft.NET.WebAssembly.Threading. I also have the workload wasm-experimental installed.
In a Blazor WASM threading project replace the Counter.razor page with the below code.
@page "/counter"
<PageTitle>Counter</PageTitle>
<h1>Counter</h1>
<p role="status">Current text: @screenData</p>
<button class="btn btn-primary" @onclick="ButtonClicked">Use InvokeAsync</button>
@code {
private string screenData = "";
// This does not work using InvokeAsync
private async void ButtonClicked()
{
Console.WriteLine("Message1,MyThreadID:" + System.Threading.Thread.CurrentThread.ManagedThreadId);
screenData = "AfterMessage1";
StateHasChanged();
await Task.Run(async () =>
{
Console.WriteLine("Message2,MyThreadID:" + System.Threading.Thread.CurrentThread.ManagedThreadId);
await Task.Delay(2000);
Console.WriteLine("Message3,MyThreadID:" + System.Threading.Thread.CurrentThread.ManagedThreadId);
await InvokeAsync(() =>
{
// This is not called on the UI thread as expected
screenData = "AfterMessage3";
Console.WriteLine("Message4,MyThreadID:" + System.Threading.Thread.CurrentThread.ManagedThreadId);
// *** Below line throws errors viewable in devtools console ***
StateHasChanged();
});
await Task.Delay(2000);
Console.WriteLine("Message5,MyThreadID:" + System.Threading.Thread.CurrentThread.ManagedThreadId);
});
Console.WriteLine("Message6,MyThreadID:" + System.Threading.Thread.CurrentThread.ManagedThreadId);
screenData = "AfterMessage6";
StateHasChanged();
}
}Expected behavior
I expect InvokeAsync (in a Blazor WASM component) to call on the UI thread context.
Actual behavior
InvokeAsync (in a Blazor WASM component) calls on the same thread it is called from.
Regression?
Unknown.
Known Workarounds
This (InvokeAsyncAlt) works in place on InvokeAsync in a Blazor Component
SynchronizationContext sctx = SynchronizationContext.Current!;
Task InvokeAsyncAlt(Action action)
{
sctx.Post((object state) => action(), null);
return Task.CompletedTask;
}
Task InvokeAsyncAlt(Func<Task> action)
{
sctx.Post(async (object state) => await action(), null);
return Task.CompletedTask;
}Configuration
Blazor WebAssembly Standalone template with .Net 8.0.0
Windows 10 x64
.Net 8.0.100
Tested in Chrome 119.0.6045.200 and Firefox 120.0
Other information
I came across this issue while helping answer a question on StackOverflow How do child threads talk to the main (UI) thread in blazor webassembly (multithreading mode)