VB.NET多线程读写SQL Server表卡顿,求实时进度显示优化方案
解决VB.NET后台线程读写数据库表的延迟问题
针对你遇到的BG1写入表时BG2查询卡顿、无法实时展示进度的问题,结合表数据量极小(最多100条)的特点,给出以下几个实用解决方案:
1. 调整查询的隔离级别,允许读取未提交数据
默认的READ COMMITTED隔离级别下,BG1的写入事务会持有行锁/表锁,导致BG2的查询等待锁释放。由于进度数据不需要绝对的一致性,可直接使用NOLOCK提示或设置READ UNCOMMITTED隔离级别,跳过锁等待:
-- 使用NOLOCK提示 SELECT * FROM IMPORT.DBO.XXX_ANALYSIS_UPDATES WITH (NOLOCK) -- 或者在连接级别设置隔离级别 SET TRANSACTION ISOLATION LEVEL READ UNCOMMITTED; SELECT * FROM IMPORT.DBO.XXX_ANALYSIS_UPDATES;
这个修改成本极低,能立即减少查询等待时间。
2. 优化BG1的事务提交策略
如果BG1调用的存储过程是在一个大事务中批量插入数据,会长期持有锁。建议修改存储过程,每插入一条或几条记录就提交一次事务,缩短锁的持有时间:
-- 示例存储过程修改(伪代码) CREATE PROCEDURE InsertAnalysisUpdate @GROUPING VARCHAR(500), @SOURCE VARCHAR(500), @TARGET VARCHAR(500), @TYPE VARCHAR(500), @ACTION VARCHAR(500), @COUNTS INT AS BEGIN SET NOCOUNT ON; -- 每次插入单独提交,避免大事务锁表 BEGIN TRANSACTION; INSERT INTO IMPORT.DBO.XXX_ANALYSIS_UPDATES ([GROUPING], SOURCE, TARGET, [TYPE], ACTION, COUNTS) VALUES (@GROUPING, @SOURCE, @TARGET, @TYPE, @ACTION, @COUNTS); COMMIT TRANSACTION; END
这样BG2的查询能及时读到新插入的数据,不会被长时间阻塞。
3. 完全绕开数据库,用线程安全集合传递数据
由于表最多仅100条记录,完全不需要用数据库作为中间层。改用VB.NET的线程安全集合(如ConcurrentBag(Of T))在BG1和UI线程之间传递数据,彻底消除数据库锁和IO开销,实现真正的实时展示:
步骤1:定义数据实体类
Public Class AnalysisUpdate Public Property ID As Integer Public Property Grouping As String Public Property Source As String Public Property Target As String Public Property Type As String Public Property Action As String Public Property Counts As Integer End Class
步骤2:声明全局线程安全集合
在窗体类中定义共享集合,供BG1和Timer线程访问:
Private Shared _progressUpdates As New ConcurrentBag(Of AnalysisUpdate)
步骤3:BG1中直接写入集合(替代数据库插入)
' BG1的DoWork事件中,替换原存储过程调用代码 _progressUpdates.Add(New AnalysisUpdate With { .Grouping = "你的分组值", .Source = "源数据标识", .Target = "目标数据标识", .Type = "操作类型", .Action = "具体动作", .Counts = 统计数量 })
步骤4:Timer中读取集合并更新ListBox
Private Sub Timer1_Tick(sender As Object, e As EventArgs) Handles Timer1.Tick Dim newUpdates As New List(Of AnalysisUpdate) Dim updateItem As AnalysisUpdate ' 批量取出集合中的新数据 While _progressUpdates.TryTake(updateItem) newUpdates.Add(updateItem) End While ' 跨线程更新UI,必须使用Invoke避免线程异常 If newUpdates.Count > 0 Then ListBox1.Invoke(Sub() For Each item In newUpdates ListBox1.Items.Add($"{item.Grouping} | {item.Action}: {item.Counts}") Next End Sub) End If End Sub
这个方案性能最优,完全避免了数据库相关的瓶颈,是小量进度数据传递的首选。
4. 确认表的主键索引
虽然表数据量小,但如果ID列未设置为主键,查询会进行全表扫描,增加耗时。执行以下语句确认并添加主键:
ALTER TABLE IMPORT.DBO.XXX_ANALYSIS_UPDATES ADD PRIMARY KEY (ID);
主键索引会让查询更快,即使存在锁,扫描时间也会大幅缩短。
内容的提问来源于stack exchange,提问作者Always Try to Learn More
相关产品推荐
相关产品推荐

