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

.NET数据加载优化:DataGridView与SQLite异步加载性能问题

解决DataGridView异步加载SQLite数据及刷新性能问题

看起来你遇到的核心问题是串行异步操作导致的耗时累加,再加上一些SQLite连接管理、UI更新的小问题,才让21个玩家的刷新耗时拉到了7秒。下面针对你的场景给出具体的优化方案,帮你把20个玩家的刷新时间压到1秒以内:


1. 把串行异步改成并行批量处理(最核心的优化)

你的UpdateAllPlayers方法是循环里逐个await GetPlayerInfo,这相当于串行执行所有HTTP请求+数据库更新,每个请求都要等上一个完成,时间自然是累加的。改成并行发起所有任务,总耗时会接近单个请求的时间,而不是总和。

修改后的代码:

public async Task UpdateAllPlayers()
{
    var allPlayers = await database.GetAllPlayers();
    // 把所有更新任务收集起来,用Task.WhenAll并行执行
    var updateTasks = allPlayers.Select(player => 
        GetPlayerInfo(player.Name, player.ID, player.Type, true));
    await Task.WhenAll(updateTasks);
}

额外小优化:控制并发数避免限流

如果玩家数量扩展到120个,直接发起120个HTTP请求可能会被目标网站限流,或者导致本地网络压力过大。可以用SemaphoreSlim限制并发数:

// 定义一个全局的信号量,限制最多20个并发请求
private readonly SemaphoreSlim _httpRequestSemaphore = new SemaphoreSlim(20);

public async Task GetPlayerInfo(string name, int id, string type, bool flag)
{
    await _httpRequestSemaphore.WaitAsync();
    try
    {
        // 你的GetPlayerInfo原有逻辑:HTTP请求+数据库更新
        var website = $"website.com/playerdata/{id}.json";
        var playerData = await GetUserDataFromJSONAsync(website); // 改成异步方法
        await database.UpdatePlayer(playerData);
    }
    finally
    {
        _httpRequestSemaphore.Release(); // 无论成功失败都释放信号量
    }
}

2. 优化SQLite连接管理,减少连接开销

你的GetAllPlayers方法每次查询后都关闭连接,这会导致每次操作都要重新打开连接——连接的打开/关闭是有固定开销的。SQLite适合长期保持单个连接(只要配置正确的同步模式),不需要每次用完就关。

修改GetAllPlayers:

// 假设dbConnection是类级别的全局连接,在程序启动时打开,退出时再关闭
public async Task<List<Player>> GetAllPlayers()
{
    var query = dbConnection.Table<Player>();
    return await query.ToListAsync();
    // 移除await conn.CloseAsync(); 不要在这里关闭连接
}

注意:SQLite异步操作需要确保连接字符串设置了合适的参数,比如:"Data Source=yourdb.sqlite;Cache=Shared;Mode=ReadWriteCreate",避免线程冲突。


3. 优化DataGridView的更新方式,避免界面卡顿

清空再重新加载的方式会导致UI频繁重绘,产生卡顿。推荐两种优化方式:

方式一:用BindingList作为数据源,自动刷新

初始化时把DataGridView的数据源设置为BindingList<Player>,之后更新数据时直接操作BindingList,UI会自动刷新,不需要清空再绑定:

// 初始化时设置数据源
private void Form_Load(object sender, EventArgs e)
{
    var playerList = await database.GetAllPlayers();
    dataGridView1.DataSource = new BindingList<Player>(playerList);
}

// 添加玩家后直接更新BindingList
public async void btnAddPlayer_Click(object sender, EventArgs e)
{
    string website = "website.com/playerdata.json";
    var player = await GetUserDataFromJSONAsync(website);
    await database.AddPlayer(player);
    
    var bindingList = (BindingList<Player>)dataGridView1.DataSource;
    bindingList.Add(player);
}

方式二:刷新时暂停UI绘制

如果必须重新绑定数据源,先暂停DataGridView的绘制,完成后再恢复,减少卡顿:

private async Task RefreshDataGridView()
{
    dataGridView1.SuspendLayout();
    try
    {
        var allPlayers = await database.GetAllPlayers();
        dataGridView1.DataSource = null;
        dataGridView1.DataSource = allPlayers;
    }
    finally
    {
        dataGridView1.ResumeLayout();
    }
}

4. 把最高价格计算交给SQLite,减少内存循环

你的Function里是在内存中遍历Price找最高值,其实可以直接在SQLite查询时完成计算,让数据库帮你做这个工作,比内存循环高效得多:

// 假设Price表和Player表通过PlayerID关联
var query = from player in dbConnection.Table<Player>()
            join price in dbConnection.Table<Price>() on player.ID equals price.PlayerID
            group price by player into playerGroup
            select new Player
            {
                ID = playerGroup.Key.ID,
                Name = playerGroup.Key.Name,
                // 直接在数据库计算最高价格和对应日期
                HighestPrice = playerGroup.Max(p => p.Price),
                HighestPriceDate = playerGroup
                    .Where(p => p.Price == playerGroup.Max(p2 => p2.Price))
                    .Select(p => UnixToUTC(p.UnixTime))
                    .FirstOrDefault()
            };
var allPlayers = await query.ToListAsync();

5. 优化添加玩家的异步逻辑

把btnAddPlayer里的同步HTTP请求改成异步,避免阻塞UI线程;同时用HttpClient代替HttpWebRequest,异步支持更友好(注意不要每次创建新的HttpClient,用静态实例复用):

// 全局复用HttpClient,不要每次创建
private static readonly HttpClient _httpClient = new HttpClient();

public async void btnAddPlayer_Click(object sender, EventArgs e)
{
    string website = "website.com/playerdata.json";
    var player = await GetUserDataFromJSONAsync(website);
    await database.AddPlayer(player);
    
    // 刷新UI(用BindingList的话直接Add即可)
    var bindingList = (BindingList<Player>)dataGridView1.DataSource;
    bindingList.Add(player);
}

private async Task<Player> GetUserDataFromJSONAsync(string url)
{
    var json = await _httpClient.GetStringAsync(url);
    return JsonConvert.DeserializeObject<Player>(json);
}

按照这些优化点调整后,20个玩家的刷新时间应该能控制在1秒以内——并行HTTP请求会把原来的串行耗时压缩到单个请求的时间,再加上连接管理和UI更新的优化,完全能达到你的预期。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.07 07:47:35