Come funziona il Compito.Rendimento di lavoro sotto il cofano in Blazor WebAssembly?

0

Domanda

Come si fa Task.Yield lavorare sotto il cofano in Mono/WASM runtime (che è utilizzato da Blazor WebAssembly)?

Per chiarire, credo di avere una buona comprensione di come Task.Yield funziona in .NET Framework e .NET Core. Mono attuazione , non ha un aspetto molto diverso, in sintesi, si tratta di questo:

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

Sorprendentemente, questo funziona in Blazor WebAssembly ancheprovare on-line):

<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, tutto questo accade nello stesso ciclo di eventi thread nel browser, quindi mi chiedo come funziona al livello inferiore.

Ho il sospetto, si potrebbe essere utilizzando qualcosa come Emscripten del Asyncify, ma alla fine, fa uso di una sorta di Web API della Piattaforma per pianificare una continuazione di callback? E se sì, quale esattamente (come queueMicrotask, setTimout, Promise.resove().then, etc)?


Aggiornamento, ho appena scoperto che Thread.Sleep è implementato come bene e in realtà blocca il ciclo di eventi del thread

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

Migliore risposta

5

È setTimeout. C'è una considerevole riferimento indiretto tra quella e QueueUserWorkItem, ma questo è dove si tocca.

La maggior parte delle WebAssembly-macchinari specifici, può essere visto in PR 38029. Il WebAssembly attuazione di RequestWorkerThread chiama un metodo privato denominato QueueCallbackche è implementato in C codice mono_wasm_queue_tp_cb. Questo richiama mono_threads_schedule_background_jobche a sua volta chiama schedule_background_execche viene attuata in forma scritta come:

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

Il setTimeout richiamata, infine, raggiunge ThreadPool.Callback, che richiama ThreadPoolWorkQueue.Dispatch.

Il resto non è specifico per Blazor a tutti, e può essere studiato mediante la lettura del codice sorgente del ThreadPoolWorkQueue classe. In breve, ThreadPool.QueueUserWorkItem accoda la callback in una ThreadPoolQueue. Il prodotto chiamate EnsureThreadRequestedche delega RequestWorkerThreadimplementato come sopra. ThreadPoolWorkQueue.Dispatch provoca un certo numero di attività asincrone essere annullato ed eseguite; tra di essi, la richiamata passato QueueUserWorkItem alla fine dovrebbe apparire.

2021-11-28 11:17:30

Una grande risposta, tks! Ma geven è setTimeoutpotresti spiegare un enorme discrepanza sto vedendo quando l'intervallo di un ciclo di await new Promise(r => setTimeout(r, 0)) con JS interoperabilità vs un ciclo di await Task.Yield? C'è un difetto nel test? blazorrepl.telerik.com/QlFFQLPF08dkYRbm30
noseratio

queueMicrotask (invece di setTimeout) produce una più stretta risultato: blazorrepl.telerik.com/QFbFGVFP10NWGSam57
noseratio

Io sono in grado di aprire qualsiasi REPL link, quindi non posso dire quello che vuoi dire. Ma se si studia il codice sorgente di ThreadPoolWorkQueue.Dispatchsi noterà che c'è qualche sofisticato di pianificazione coinvolti, come pure, e un setTimeout può servire più di una in coda .NETTO delle attività asincrone, che mi aspetto di essere più veloce di ogni setTimeout la spedizione di una singola richiamata.
user3840170

Strano repl link non funzionano. Se ti piace ancora di provare, ecco il nocciolo della questione: gist.github.com/noseratio/73f6cd2fb328387ace2a7761f0b0dadc. È literrally 8000ms vs 20ms. Basta sostituire setTimeout con queueMicrotaske ' lo stesso 20ms.
noseratio

È come sembra: setTimeout rende il processo del browser il ciclo di eventi tra i callback, ma il .NET runtime può inviare più attività asincrone in un unico setTimeout richiamata (l'annullamento dell'accodamento quasi immediatamente dopo che sono in coda), evitando così il sovraccarico di cedere il ciclo di eventi. (Inoltre, i browser possono eseguire la regolazione in setTimeout chiamate, che questo dosaggio evita.) Questo produce un effetto equivalente a queueMicrotask. Anche se i tempi di latenza che si ottengono sono, probabilmente, non è molto preciso, grazie a Spectre attenuazioni.
user3840170

In altre lingue

Questa pagina è in altre lingue

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