大圣又归来 發表於 2026-3-5 17:40:00

.NET SqlSugar多线程下SqlSugarClient 的线程安全陷阱

<p>使用SqlSugar读取Sqlite数据库,项目运行过程中间歇性抛出以下异常:</p>
<p><span style="color: rgba(255, 0, 0, 1)">SqlSugar.SqlSugarException:“中文提示 : 连接数据库过程中发生错误,检查服务器是否正常连接字符串是否正确,错误信息:Connection was closed, statement was terminatedDbType="Sqlite";ConfigId="".</span><br><span style="color: rgba(255, 0, 0, 1)">English Message : Connection open error . Connection was closed, statement was terminatedDbType="Sqlite";ConfigId="" ”</span></p>
<p><span style="color: rgba(0, 0, 0, 1)">经定位,异常抛出位置在一个很普通的查询操作上:</span></p>
<div class="cnblogs_code">
<pre><span style="color: rgba(0, 0, 255, 1)">public</span> List&lt;SqliteDiskEntity&gt; GetDisksByPDiskId(<span style="color: rgba(0, 0, 255, 1)">string</span> env, <span style="color: rgba(0, 0, 255, 1)">string</span> project, <span style="color: rgba(0, 0, 255, 1)">int</span><span style="color: rgba(0, 0, 0, 1)"> pDiskId)
{
    </span><span style="color: rgba(0, 0, 255, 1)">return</span> _db.Queryable&lt;SqliteDiskEntity&gt;().Where(x =&gt; x.PDiskId ==<span style="color: rgba(0, 0, 0, 1)"> pDiskId).ToList();
}</span></pre>
</div>
<p>连接字符串没有问题,数据库文件也正常存在,且异常并非每次必现,而是偶发性的。</p>
<h3>排查过程</h3>
<p>1. 排除连接字符串问题&nbsp;</p>
<p>连接配置如下,看起来没有明显问题:</p>
<div class="cnblogs_code">
<pre><span style="color: rgba(0, 128, 128, 1)">1</span> <span style="color: rgba(0, 0, 255, 1)">private</span> <span style="color: rgba(0, 0, 255, 1)">static</span> SqlSugarClient CreateClient(<span style="color: rgba(0, 0, 255, 1)">string</span><span style="color: rgba(0, 0, 0, 1)"> dbPath)
</span><span style="color: rgba(0, 128, 128, 1)">2</span> <span style="color: rgba(0, 0, 0, 1)">{
</span><span style="color: rgba(0, 128, 128, 1)">3</span>   <span style="color: rgba(0, 0, 255, 1)">return</span> <span style="color: rgba(0, 0, 255, 1)">new</span> SqlSugarClient(<span style="color: rgba(0, 0, 255, 1)">new</span><span style="color: rgba(0, 0, 0, 1)"> ConnectionConfig
</span><span style="color: rgba(0, 128, 128, 1)">4</span> <span style="color: rgba(0, 0, 0, 1)">    {
</span><span style="color: rgba(0, 128, 128, 1)">5</span>         ConnectionString = $<span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(128, 0, 0, 1)">Data Source={dbPath};Version=3;Journal Mode=Wal;BusyTimeout=5000;</span><span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(0, 0, 0, 1)">,
</span><span style="color: rgba(0, 128, 128, 1)">6</span>         DbType =<span style="color: rgba(0, 0, 0, 1)"> SqlSugar.DbType.Sqlite,
</span><span style="color: rgba(0, 128, 128, 1)">7</span>         IsAutoCloseConnection = <span style="color: rgba(0, 0, 255, 1)">true</span><span style="color: rgba(0, 0, 0, 1)">,
</span><span style="color: rgba(0, 128, 128, 1)">8</span> <span style="color: rgba(0, 0, 0, 1)">    });
</span><span style="color: rgba(0, 128, 128, 1)">9</span> }</pre>
</div>
<p>已开启 WAL 模式、设置了 BusyTimeout、配置了 IsAutoCloseConnection = true,表面上不应该出问题。</p>
<p>2. 调试器线程分析 —— 发现并发访问</p>
<p>在 Visual Studio 中暂停调试,查看线程窗口时发现了关键线索:</p>
<p>线程 ID 当前位置<br>34996        DiskSubscribeTimer_Triggered(object, SubscribeTaskArgs)<br>30716        ExecuteSubscribeTask(SubscribeTaskArgs) → RunSubscribeTaskAsync()<br>38276        DiskSubscribeTimer_Triggered(object, SubscribeTaskArgs)</p>
<p>虽然代码中用 ConcurrentDictionary 防止了同一个磁盘的并发执行,但不同的订阅任务仍然会并行运行,共享同一个 _db 实例。</p>
<h3>问题根因及解决</h3>
<p>_db是SqlSugarClient实例,所以,</p>
<p>此实例不是线程安全的。 当多个线程同时通过同一个 SqlSugarClient 实例操作数据库时,内部的连接/命令对象会发生竞争,导致 SQLite 底层返回 SQLITE_MISUSE(即 bad parameter or other API misuse)。</p>
<p>整个调用链路如下:</p>
<p><img src="https://img2024.cnblogs.com/blog/685541/202603/685541-20260305173632823-35311381.png" alt="image" loading="lazy"></p>
<p>这个错误的迷惑性在于:</p>
<ul>
<li>连接字符串完全正确</li>
<li>IsAutoCloseConnection = true 看似已经处理了连接释放</li>
<li>异常只在多任务并发时偶发出现</li>
<li>异常信息指向"连接错误",容易误导排查方向</li>


</ul>
<p>将SqlSugarClient(非线程安全)改为SqlSugarScope(线程安全),即可</p>
<p>SqlSugarClient vs SqlSugarScope的区别,列个对比</p>
<p><img src="https://img2024.cnblogs.com/blog/685541/202603/685541-20260305173822737-1717976316.png" alt="image" loading="lazy"></p>
<p>&nbsp;</p>

</div>
<div id="MySignature" role="contentinfo">
    <div>作者:唐宋元明清2188</div>
<div>出处:http://www.cnblogs.com/kybs0/</div>
<div>让学习成为习惯,假设明天就有重大机遇等着你,你准备好了么</div>
<div>本文版权归作者和博客园共有,欢迎转载,但未经作者同意必须在文章页面给出原文连接,否则保留追究法律责任的权利。 </div><br><br>
来源:https://www.cnblogs.com/kybs0/p/19674369
頁: [1]
查看完整版本: .NET SqlSugar多线程下SqlSugarClient 的线程安全陷阱