Cum Sarcina.Randament de lucru sub capota în Blazor WebAssembly?

0

Problema

Cum Task.Yield munca sub capota in Mono/WASM de execuție (care este utilizat de către Blazor WebAssembly)?

Pentru a clarifica, cred că am o bună înțelegere a cum Task.Yield funcționează în .NET Framework și .NET Core. Mono aplicare nu arata cu mult diferit, într-un cuvânt, totul se rezumă la asta:

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

În mod surprinzător, acest lucru funcționează în Blazor WebAssembly, prea (încercați să-l 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();
        }
    }
}

Desigur, totul se întâmplă pe același eveniment buclă de fir în browser-ul, așa că am întreb cum funcționează la nivelul inferior.

Eu bănuiesc că ar putea fi folosind ceva de genul Emscripten e Asyncify, dar în cele din urmă, se folosesc un fel de Platforma Web API pentru a programa o continuare callback? Și dacă da, care anume (cum ar fi queueMicrotask, setTimout, Promise.resove().then, etc)?


Actualizat, am descoperit că Thread.Sleep este implementat, precum și, de fapt, blocuri de bucla eveniment fir

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

Cel mai bun răspuns

5

E setTimeout. Există o considerabilă ocolitoare între care și QueueUserWorkItem, dar acest lucru este în cazul în care s-a schimbat totul.

Cele mai multe dintre WebAssembly-utilaje specifice poate fi văzut în PR 38029. La WebAssembly punerea în aplicare a RequestWorkerThread solicită o metodă privat numit QueueCallback, care este implementat în cod C ca mono_wasm_queue_tp_cb. Aceasta invocă în mono_threads_schedule_background_job, care, la rândul său, solicită schedule_background_exec, care este implementat în mașina de Scris ca:

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

La setTimeout apel invers în cele din urmă ajunge la ThreadPool.Callback, care invocă ThreadPoolWorkQueue.Dispatch.

Restul nu este specifică Blazor la toate, și pot fi studiate prin citirea codului sursă al ThreadPoolWorkQueue clasa. Pe scurt, ThreadPool.QueueUserWorkItem enqueues callback într-un ThreadPoolQueue. Enqueueing apeluri EnsureThreadRequested, care delegații la RequestWorkerThread, puse în aplicare cât mai sus. ThreadPoolWorkQueue.Dispatch unele cauze numărul de sarcini asincrone să fie dequeued și executat; printre ei, callback-a trecut la QueueUserWorkItem ar trebui să apar în cele din urmă.

2021-11-28 11:17:30

Un răspuns de mare, tks! Dar geven e setTimeout, ați putea explica o mare discrepanță văd atunci când momentul o buclă de await new Promise(r => setTimeout(r, 0)) cu JS interop vs o buclă de await Task.Yield? Există un defect în test? blazorrepl.telerik.com/QlFFQLPF08dkYRbm30
noseratio

queueMicrotask (spre deosebire de setTimeout) produce o mult mai aproape de rezultat: blazorrepl.telerik.com/QFbFGVFP10NWGSam57
noseratio

Eu sunt în imposibilitatea de a deschide oricare dintre REPL link-uri, așa că nu pot spune la ce te referi. Dar dacă veți studia codul sursă al ThreadPoolWorkQueue.Dispatch, veți observa există unele sofisticate de planificare implicate, precum și unul setTimeout poate servi mai multe din coada de așteptare .NET sarcini asincrone, care nu m-aș aștepta să fie mai rapid decât având fiecare setTimeout trimite un singur apel invers.
user3840170

Ciudat repl link-uri nu funcționează. Dacă încă doriți să încercați să-l, aici e esența: gist.github.com/noseratio/73f6cd2fb328387ace2a7761f0b0dadc. E literrally 8000ms vs 20ms. Apoi înlocuiți doar setTimeout cu queueMicrotaskși e la fel de 20ms.
noseratio

Se pare ca aceasta: setTimeout face procesul de browser-ul evenimentului buclă între callback, dar .NET runtime poate transporta mai multe sarcini asincrone într-un singur setTimeout callback (dequeuing le aproape imediat după ce acestea sunt în așteptare), evitându-se astfel costurile de cedare la bucla eveniment. (De asemenea, browsere poate efectua supraîncărcarea pe setTimeout apeluri, care în acest dozare evită.) Aceasta produce un efect aproximativ echivalent cu queueMicrotask. Deși timpii veți obține, probabil, nu sunt foarte exacte, datorită Spectre atenuare.
user3840170

În alte limbi

Această pagină este în alte limbi

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