skip to Main Content

Is there a situation where a SynchronisationContext is used over multiple threads/tasks in Blazor?

I want to determine whether a method has an components BuildRenderTree in its call stack. To do so i created a custom ComponentBase where i wrap the BuildRenderTree with custom code. My approach would be to check the SynchronisationContext. As i understood it is uniquely for one component, and therefor the execution of BuildRenderTree will not be "interrupted" by other threads/tasks with the same SynchronisationContext set. Did i get this right? Are there any pitfalls with using the SynchronisationContext for this case? (i am not sure if the components used in the razor file are rendered within the BuildRenderTree but i would handle this case correctly alreay)

2

Answers


  1. As i understood it is uniquely for one component,

    Not really. It is attached to the SignalR circuit and shared by the whole UI. It is null when running on WebAssembly.

    and therefor the execution of BuildRenderTree will not be "interrupted" by other threads/tasks with the same SynchronisationContext set.

    BuildRenderTree() is not async, it won’t be interrupted by other Tasks. It might be pre-empted by other threads but that should not affect what’s on the call stack.

    Login or Signup to reply.
  2. To quote the Microsoft Documentation:

    Blazor uses a synchronization context (SynchronizationContext) to enforce a single logical thread of execution. A component’s lifecycle methods and event callbacks raised by Blazor are executed on the synchronization context.

    Blazor’s server-side synchronization context attempts to emulate a single-threaded environment so that it closely matches the WebAssembly model in the browser, which is single threaded. This emulation is scoped only to an individual circuit, meaning two different circuits can run in parallel. At any given point in time within a circuit, work is performed on exactly one thread, which yields the impression of a single logical thread. No two operations execute concurrently within the same circuit.

    However, this won’t help you. No event triggered by a user action or method you call will have BuildRenderTree in it’s call stack. Events and user defined methods queue RenderFragments on the Renderer’s Render queue by calling StateHasChanged.

    Here’s the code:

    protected void StateHasChanged()
        {
            if (_hasPendingQueuedRender)
            {
                return;
            }
    
            if (_hasNeverRendered || ShouldRender() || _renderHandle.IsRenderingOnMetadataUpdate)
            {
                _hasPendingQueuedRender = true;
    
                try
                {
                    _renderHandle.Render(_renderFragment);
                }
                catch
                {
                    _hasPendingQueuedRender = false;
                    throw;
                }
            }
        }
    

    The Renderer services the queue and posts RenderFragments placed on the queue to the Synchronisation Context.

    Here’s the RenderFragment containing the call to BuildRenderTree – defined in the ComponentBase constructor:

        public ComponentBase()
        {
            _renderFragment = builder =>
            {
                _hasPendingQueuedRender = false;
                _hasNeverRendered = false;
                BuildRenderTree(builder);
            };
        }
    
    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search