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

多线程读取SQLite数据准确率下降且报InvalidOperationException如何解决

问题根因

你的核心问题是类成员变量m_dbConnLocal被多线程共享导致竞态条件:
所有线程调用DownloadListSiteFromDB时都会修改同一个全局的m_dbConnLocal变量指向,比如线程1刚把它赋值为ID25的连接,线程2立刻将它改写为ID27的连接,后续线程1执行Open、查询等操作时实际操作的是被篡改后的连接,自然会出现连接状态异常、数据读取错误的问题。

解决方案

首先明确:改用Dictionary<int, DbConnection>存储连接是合理的,但这只是优化代码结构的手段,核心要解决的是不能用共享成员变量传递当前操作的连接,必须用方法内部的局部变量存储对应连接,彻底切断多线程修改共享变量的路径。
完整修改步骤如下:

  1. 删除全局共享的m_dbConnLocal成员变量,改用Dictionary<int, SQLiteConnection>存储预创建的所有连接,替代冗长的if-else判断,初始化示例:
// 类初始化阶段构造连接字典,替代原来的单个连接变量定义
private readonly Dictionary<int, SQLiteConnection> _d10ConnDict = new Dictionary<int, SQLiteConnection>
{
    {1, new SQLiteConnection("Data Source=\\\\sklplfs03\\D10Devprogs\\D10_Monitor\\D10_1.db;version=3;new=False;Timeout=30000;datetimeformat=CurrentCulture")},
    {3, new SQLiteConnection("Data Source=\\\\sklplfs03\\D10Devprogs\\D10_Monitor\\D10_3.db;version=3;new=False;Timeout=30000;datetimeformat=CurrentCulture")},
    // 其余D10ID对应的连接按上述格式依次添加即可
};
  1. 调整DownloadListSiteFromDB的连接获取逻辑,直接从字典取连接赋值给局部变量,完全不涉及共享变量修改:
public bool DownloadListSiteFromDB(List<StructB> list, Int32 D10ID)
{
    // 从字典取对应连接,赋值给方法内局部变量,不会被其他线程修改
    if (!_d10ConnDict.TryGetValue(D10ID, out SQLiteConnection localConn))
    {
        DataProcessor.WriteLog("参数错误", $"DownloadListSiteFromDB: 未找到D10ID={D10ID}对应的数据库连接");
        return false;
    }
    
    try
    { 
        string sql = @" 你的原SQL语句保持不变即可 ";
        using (SQLiteCommand command = new SQLiteCommand(sql, localConn))
        {
            // 加状态判断避免重复打开已打开的连接
            if (localConn.State != System.Data.ConnectionState.Open)
            {
                localConn.Open();
            }
            using (SQLiteDataReader reader = command.ExecuteReader())
            {
                // 你的原数据读取逻辑保持不变即可
            }
        }
    }
    catch (Exception ex)
    {
        DataProcessor.WriteLog("Error Catching", "DownloadListSiteFromDB: DB Error D1 - Exception: " + D10ID + " " + ex.Message);
        return false;
    }
    // 注意:如果每个D10ID仅对应唯一线程访问,不需要每次查询都关闭连接,保持常开性能更好
    // 如果需要关闭,也操作局部变量localConn,不要操作任何共享变量
    finally
    {
        // localConn.Close();
    }
    return true;
}
额外优化建议
  • SQLite的读操作本身是线程安全的,但连接对象不是线程安全的,你当前每个D10ID对应独立连接、独立线程的架构是合理的,只要避免共享连接变量就不会有并发问题
  • 后续新增D10ID只需要在连接字典里加一条配置即可,不需要修改大量if-else判断,维护成本更低

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.09.28 13:24:04