Como Tarefa.Rendimento de trabalho sob o capô em Blazor WebAssembly?

0

Pergunta

Como funciona Task.Yield trabalho sob o capô em Mono/WASM de tempo de execução (que é usado pelo Blazor WebAssembly)?

Para esclarecer, eu acredito que eu tenha um bom entendimento de como Task.Yield funciona em .NET Framework e .NET Núcleo. Mono implementação não parece muito diferente, em poucas palavras, tudo se resume a isto:

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

Surpreendentemente, isso funciona em Blazor WebAssembly, também (experimentá-lo 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, tudo acontece no mesmo ciclo de eventos thread no navegador, então eu me pergunto como ele funciona no nível inferior.

Eu suspeito, ele pode ser utilizando algo como Emscripten do Asyncify, mas, eventualmente, faz uso de algum tipo de Plataforma Web API para agendar uma continuação chamada de retorno? E se sim, qual exatamente (como queueMicrotask, setTimout, Promise.resove().then, etc)?


Atualizado, eu apenas descobri que Thread.Sleep é implementado como bem e ele realmente bloqueia o ciclo de eventos thread

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

Melhor resposta

5

É setTimeout. Existe uma indireção entre o que e QueueUserWorkItemmas este é o lugar onde o fundo.

A maioria dos WebAssembly-máquinas específicas pode ser visto no PR 38029. O WebAssembly implementação de RequestWorkerThread chama um método privado chamado QueueCallback, que é implementado em C código mono_wasm_queue_tp_cb. Isso nos chama mono_threads_schedule_background_job, que por sua vez chama schedule_background_exec, que é implementado no TypeScript como:

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

O setTimeout de retorno de chamada, eventualmente, atinge ThreadPool.Callback, que chama ThreadPoolWorkQueue.Dispatch.

O resto não é específico para Blazor em tudo, e pode ser estudado através da leitura do código-fonte do ThreadPoolWorkQueue de classe. Em suma, ThreadPool.QueueUserWorkItem enfileira a chamada de retorno em um ThreadPoolQueue. Enqueueing chamadas EnsureThreadRequested, que delega a RequestWorkerThread, implementado como acima. ThreadPoolWorkQueue.Dispatch faz com que um certo número de tarefas assíncronas para ser será e executado; entre eles, a chamada de retorno passada para QueueUserWorkItem deve, eventualmente, aparecer.

2021-11-28 11:17:30

Uma grande resposta, tks! Mas geven é setTimeout, você poderia explicar um enorme discrepância eu estou a ver, quando o tempo de um ciclo de await new Promise(r => setTimeout(r, 0)) a galeria de interoperabilidade vs um ciclo de await Task.Yield? Existe uma falha no teste? blazorrepl.telerik.com/QlFFQLPF08dkYRbm30
noseratio

queueMicrotask (ao contrário setTimeout) produz muito mais resultado: blazorrepl.telerik.com/QFbFGVFP10NWGSam57
noseratio

Eu sou incapaz de abrir qualquer um dos REPL links, então não posso dizer o que você quer dizer. Mas se você estudar o código-fonte de ThreadPoolWorkQueue.Dispatch, você vai notar que há alguns sofisticados de programação envolvidas, e uma setTimeout podem servir várias fila de espera .NET tarefas assíncronas, o que gostaria de esperar para ser mais rápido do que ter cada setTimeout expedição de uma única chamada de retorno.
user3840170

Estranho repl links não funcionam. Se você ainda quiser tentar, aqui é a essência: gist.github.com/noseratio/73f6cd2fb328387ace2a7761f0b0dadc. É literrally 8000ms vs 20ms. Em seguida, basta substituir setTimeout com queueMicrotaske é sobre o mesmo 20ms.
noseratio

Parece que ele: setTimeout faz com que o navegador a processar o ciclo de eventos entre os retornos de chamada, mas o .Tempo de execução NET pode despacho várias tarefas assíncronas em um único setTimeout chamada de retorno (descarregamento-los quase que imediatamente depois que eles são colocados em fila), evitando, assim, a sobrecarga de paragem para o loop de eventos. (Além disso, os navegadores podem realizar a otimização de setTimeout chamadas, que este lote evita.) Isso produz um efeito mais ou menos equivalente a queueMicrotask. Embora os intervalos que você tem são, provavelmente, não muito precisas, graças ao Espectro de atenuação.
user3840170

Em outros idiomas

Esta página está em outros idiomas

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