线程内SqlConnection引发终结线程阻塞的原因分析
问题描述
我们有一个与主线程并行读取数据库的CardHandligSpeedometer类,代码如下:
public class CardHandligSpeedometer : IDisposable { private Thread m_Thread; public CardHandligSpeedometer() { createThread(); } public void StartThread() { m_IsStopped = false; if (m_Thread == null) { createThread(); } m_Thread.Start(); } public void StopThread() { m_IsStopped = true; if (m_Thread != null) { m_Thread.Join(500); if (m_Thread != null && m_Thread.IsAlive) { m_Thread.Abort(); } m_Thread = null; } } public void Dispose() { StopThread(); } void getHandledCard() { while (!m_IsStopped) { try { using (SqlConnection connection = new SqlConnection(LetFlowLib.FactoryData.StrCnt)) { connection.Open(); using (SqlCommand cmd = new SqlCommand("GetHandledCardCountFor2Hours", connection)) { // 业务代码 } Thread.Sleep(m_Interval); } } catch (Exception ex) { // 异常处理代码 } } } private void createThread() { m_Thread = new Thread(getHandledCard); m_Thread.SetApartmentState(ApartmentState.MTA); m_Thread.IsBackground = true; } }
主线程中创建并启动该线程的代码:
m_CardHandlingSpeedometer = gcnew ltr_GUI::CardHandligSpeedometer(_CTX.GetAppointID(), 30000); m_CardHandlingSpeedometer->StartThread();
遇到的问题:
- 首次读取数据库并退出
using语句后,终结线程出现阻塞; - 若在主线程调用
GC::Collect(2);和GC::WaitForPendingFinalizers();,程序会在WaitForPendingFinalizers()方法处冻结; - 将
createThread()方法中的线程单元状态从STA改为MTA后,一切恢复正常。
请问该现象的原因是什么?
原因分析
STA线程的消息循环强制要求STA(单线程单元)模式的线程必须维护活跃的消息循环,才能处理对象终结、非托管资源释放等依赖消息队列的操作。你的工作线程在执行完数据库操作后进入Thread.Sleep()状态,期间无法处理消息队列中的任务:- 当
using语句释放SqlConnection等资源时,相关的清理消息无法被STA线程处理,导致线程终结流程阻塞; - 调用
GC::WaitForPendingFinalizers()时,垃圾回收器需要等待STA线程处理终结器的执行请求,但线程处于休眠状态无法响应,最终造成主线程冻结。
- 当
MTA线程适配后台无消息场景MTA(多线程单元)模式的线程没有消息循环的强制要求,适合执行数据库访问、后台计算这类无需处理UI消息的任务。MTA线程在释放非托管资源时,不需要依赖自身的消息队列,资源回收和线程终结流程可以正常推进,因此不会出现阻塞或冻结问题。额外的代码隐患
代码中使用Thread.Abort()强制终止线程的做法风险很高,可能导致数据库连接等资源无法正常释放,甚至引发未知异常。建议仅通过m_IsStopped标记让线程自行退出循环,采用协作式终止的方式保证资源安全回收。
内容的提问来源于stack exchange,提问作者SullenMan
相关产品推荐
相关产品推荐

