繁体   English   中英

Task.Yield 在 Blazor WebAssembly 中是如何工作的?

[英]How does Task.Yield work under the hood in Blazor WebAssembly?

Task.YieldMono/WASM 运行时(由 Blazor WebAssembly 使用)中如何工作?

澄清一下,我相信我对Task.Yield在 .NET 框架和 .NET 核心中的工作原理有很好的理解。 Mono 的实现看起来并没有太大的不同,简而言之,它归结为:

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

令人惊讶的是,这也适用于 Blazor WebAssembly(在线尝试):

<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();
        }
    }
}

自然,这一切都发生在浏览器中的同一个事件循环线程上,所以我想知道它在较低级别是如何工作的。

我怀疑,它可能使用了Emscripten 的 Asyncify 之类的东西,但最终,它是否使用某种 Web 平台 API 来安排继续回调? 如果是这样,究竟是哪一个(如queueMicrotasksetTimoutPromise.resove().then等)?


更新了,我刚刚发现Thread.Sleep也实现了,它实际上阻塞了事件循环线程

也很好奇它在 WebAssembly 级别上是如何工作的。 使用 JavaScript,我只能想到一个繁忙的循环来模拟Thread.Sleep (因为Atomics.wait只能从 web 工作线程获得)。

它是setTimeout 这和QueueUserWorkItem之间有相当大的间接性,但这是它触底的地方。

大多数 WebAssembly 特定的机器可以在PR 38029中看到。 RequestWorkerThread的 WebAssembly 实现调用了一个名为QueueCallback的私有方法,该方法在 C 代码中实现为mono_wasm_queue_tp_cb 这会调用mono_threads_schedule_background_job ,后者又调用schedule_background_exec ,它在 TypeScript 中实现为:

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

setTimeout回调最终到达ThreadPool.Callback ,它调用ThreadPoolWorkQueue.Dispatch

它的rest根本不是Blazor特有的,可以通过阅读ThreadPoolWorkQueue class的源码来研究。 简而言之, ThreadPool.QueueUserWorkItem将回调加入到ThreadPoolQueue中。 Enqueueing 调用EnsureThreadRequested ,它委托给RequestWorkerThread ,如上实现。 ThreadPoolWorkQueue.Dispatch使传递给QueueUserWorkItem的回调出列并执行。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM