Cómo se hace la Tarea.El rendimiento de trabajo bajo el capó en Blazor WebAssembly?

0

Pregunta

¿Cómo Task.Yield el trabajo bajo el capó en Mono/WASM tiempo de ejecución (que es utilizado por Blazor WebAssembly)?

Para aclarar, yo creo que tengo una buena comprensión de cómo Task.Yield obras en .NET Framework y .NET Core. Mono aplicación no es muy distinto, en pocas palabras, se trata de lo siguiente:

static Task Yield() 
{
    var tcs = new TaskCompletionSource<bool>();
    System.Threading.ThreadPool.QueueUserWorkItem(_ => tcs.TrySetResult(true));
    return tcs.Task;
}

Sorprendentemente, esto funciona en Blazor WebAssembly, demasiado (intentarlo en línea):

<label>Tick Count: @tickCount</label><br>

@code 
{
    int tickCount = System.Environment.TickCount;

    protected override void OnAfterRender(bool firstRender)
    {
        if (firstRender) CountAsync();
    }

    static Task Yield() 
    {
        var tcs = new TaskCompletionSource<bool>();
        System.Threading.ThreadPool.QueueUserWorkItem(_ => tcs.TrySetResult(true));
        return tcs.Task;
    }

    async void CountAsync() 
    {
        for (var i = 0; i < 10000; i++) 
        {
            await Yield();
            tickCount = System.Environment.TickCount;
            StateHasChanged();
        }
    }
}

Naturalmente, todo sucede en el mismo bucle de eventos hilo en el navegador, así que me pregunto cómo funciona en el nivel inferior.

Sospecho que podría ser la utilización de algo como Emscripten del Asyncify, pero con el tiempo, el uso de algún tipo de Plataforma Web API para programar una continuación de devolución de llamada? Y si es así, que uno exactamente (como queueMicrotask, setTimout, Promise.resove().then, etc)?


Actualizado, he descubierto que Thread.Sleep se implementa así y que en realidad se bloquea el bucle de eventos hilo

.net async-await blazor c#
2021-11-24 06:13:47
1

Mejor respuesta

5

Es setTimeout. Existe una considerable indirección entre eso y QueueUserWorkItempero aquí es donde surge.

La mayoría de los WebAssembly-maquinaria específica, puede ser visto en PR 38029. El WebAssembly aplicación de RequestWorkerThread llama a un método privado con nombre QueueCallback, el cual es implementado en código C como mono_wasm_queue_tp_cb. Esta en la invoca mono_threads_schedule_background_job, que a su vez llama schedule_background_exec, el cual es implementado en el Manuscrito como:

export function schedule_background_exec(): void {
    ++pump_count;
    if (typeof globalThis.setTimeout === "function") {
        globalThis.setTimeout(pump_message, 0);
    }
}

El setTimeout devolución de llamada llega a la ThreadPool.Callback, que invoca ThreadPoolWorkQueue.Dispatch.

El resto no es específico para Blazor a todos, y puede ser estudiado mediante la lectura del código fuente de la ThreadPoolWorkQueue clase. En definitiva, ThreadPool.QueueUserWorkItem pone en cola la devolución de llamada en un ThreadPoolQueue. Enqueueing llamadas EnsureThreadRequestedque los delegados a RequestWorkerThread, implementado como anteriormente. ThreadPoolWorkQueue.Dispatch hace algún número de tareas asincrónicas para ser descargados y ejecutados; entre ellas, la de devolución de llamada pasa a QueueUserWorkItem finalmente, debe aparecer.

2021-11-28 11:17:30

Una gran respuesta, tks! Pero geven es setTimeoutpodría usted explicar una gran discrepancia estoy viendo cuando el tiempo de un bucle de await new Promise(r => setTimeout(r, 0)) con JS interoperabilidad vs un bucle de await Task.Yield? Hay un error en la prueba? blazorrepl.telerik.com/QlFFQLPF08dkYRbm30
noseratio

queueMicrotask (en contraposición a la setTimeout), que produce una mucho más cerca de resultado: blazorrepl.telerik.com/QFbFGVFP10NWGSam57
noseratio

Soy incapaz de abrir cualquiera de los REPL enlaces, así que no puedo decir lo que quieres decir. Pero si usted estudia el código fuente de ThreadPoolWorkQueue.Dispatch, notará que hay algunos más sofisticados de la programación de la que participan también, y una setTimeout puede servir para múltiples en la cola .NET tareas asincrónicas, que yo esperaría a ser más rápido que tener cada setTimeout envío de una sola de devolución de llamada.
user3840170

Extraño repl enlaces no funcionan. Si todavía deseas probarlo, aquí está el quid: gist.github.com/noseratio/73f6cd2fb328387ace2a7761f0b0dadc. Es literrally 8000ms vs 20ms. A continuación, sólo tiene que sustituir setTimeout con queueMicrotasky se trata de la misma 20ms.
noseratio

Parece que: setTimeout hace que el proceso del explorador el bucle de eventos en entre devoluciones de llamada, pero el .NET puede enviar múltiples tareas asincrónicas en una sola setTimeout de devolución de llamada (de la cola de ellos casi inmediatamente después de que ellos están en la cola), evitando así la sobrecarga de rendimiento para el bucle de eventos. (Además, los navegadores pueden realizar la estrangulación setTimeout llamadas, que esto evita la formación de lotes.) Esto produce un efecto equivalente a queueMicrotask. A pesar de los tiempos que te dan son, probablemente, no es muy preciso, gracias a Spectre mitigaciones.
user3840170

En otros idiomas

Esta página está en otros idiomas

Русский
..................................................................................................................
Italiano
..................................................................................................................
Polski
..................................................................................................................
Română
..................................................................................................................
한국어
..................................................................................................................
हिन्दी
..................................................................................................................
Français
..................................................................................................................
Türk
..................................................................................................................
Česk
..................................................................................................................
Português
..................................................................................................................
ไทย
..................................................................................................................
中文
..................................................................................................................
Slovenský
..................................................................................................................