You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Blazor:如何实现无UI线程阻塞的对象序列化?

解决方案

1. 分块处理+主动让出UI线程

在序列化循环中,每处理一定数量的对象就主动调用await Task.Yield(),给UI线程留出处理其他任务的间隙,把长时间阻塞拆分成多个短停顿:

var reader = new Utf8JsonAsyncStreamReader(await responseMessage.Content.ReadAsStreamAsync());
var list = new List<SimplifiedPlacemark>();
var simplifiedMark = new SimplifiedPlacemark();
int processedCount = 0;

while (await reader.ReadAsync())
{
    JsonTokenType tokenType = reader.TokenType;

    switch (tokenType)
    {
        // 保留你原有的所有case逻辑
        case JsonTokenType.EndObject:
            list.Add(simplifiedMark);
            processedCount++;
            // 每处理50个对象就让出一次UI线程
            if (processedCount % 50 == 0)
            {
                await Task.Yield();
            }
            break;
    }
}

2. 后台线程序列化+分批更新UI

把序列化逻辑完全放到后台线程执行,不占用UI线程,同时分批将处理好的对象传回UI更新,避免一次性传递大量数据:

// UI线程初始化列表
var list = new List<SimplifiedPlacemark>();
var cancellationTokenSource = new CancellationTokenSource();

// 后台线程处理序列化
_ = Task.Run(async () =>
{
    var stream = await responseMessage.Content.ReadAsStreamAsync();
    var reader = new Utf8JsonAsyncStreamReader(stream);
    var simplifiedMark = new SimplifiedPlacemark();
    var batch = new List<SimplifiedPlacemark>();
    const int batchSize = 100;

    while (await reader.ReadAsync() && !cancellationTokenSource.Token.IsCancellationRequested)
    {
        JsonTokenType tokenType = reader.TokenType;

        switch (tokenType)
        {
            // 保留你原有的所有case逻辑
            case JsonTokenType.EndObject:
                batch.Add(simplifiedMark);
                if (batch.Count >= batchSize)
                {
                    // 回到UI线程更新列表(Blazor用InvokeAsync,WPF/UWP用Dispatcher.InvokeAsync)
                    await InvokeAsync(() =>
                    {
                        list.AddRange(batch);
                        StateHasChanged(); // Blazor中触发UI刷新
                    });
                    batch.Clear();
                    await Task.Delay(10); // 给UI留一点缓冲时间
                }
                break;
        }
    }

    // 处理剩余的最后一批对象
    if (batch.Count > 0)
    {
        await InvokeAsync(() =>
        {
            list.AddRange(batch);
            StateHasChanged();
        });
    }
}, cancellationTokenSource.Token);

3. 优化WebWorker的对象传输逻辑

之前WebWorker导致UI阻塞,核心是一次性传递大量对象的结构化克隆开销太大。改成WebWorker分批序列化并发送对象:

  • WebWorker端:每序列化100个对象就通过postMessage发送一批,然后清空当前批次
  • UI端:监听message事件,每次收到批次就添加到List并刷新UI
    这样能避免一次性传递海量数据带来的UI卡顿。

4. 改用原生流式序列化API

用System.Text.Json的DeserializeAsyncEnumerable原生流式API,它本身就是异步分块处理的,配合间隙让出UI线程,代码更简洁且性能更优:

var stream = await responseMessage.Content.ReadAsStreamAsync();
var jsonOptions = new JsonSerializerOptions { PropertyNameCaseInsensitive = true };
var placemarkEnumerable = JsonSerializer.DeserializeAsyncEnumerable<SimplifiedPlacemark>(stream, jsonOptions);

var list = new List<SimplifiedPlacemark>();
int processedCount = 0;

await foreach (var placemark in placemarkEnumerable)
{
    if (placemark != null)
    {
        list.Add(placemark);
        processedCount++;
        if (processedCount % 50 == 0)
        {
            await Task.Yield();
            StateHasChanged(); // Blazor中触发UI刷新
        }
    }
}

内容的提问来源于stack exchange,提问作者user2657943

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.12 04:35:39