一:背景
1. 讲故事
为什么说这东西比较坑人呢?是因为最近一个月接到了两个dump,都反应程序卡死无响应,最后分析下来是因为线程饥饿
导致,那什么原因导致的线程饥饿呢?进一步分析发现罪魁祸首是 MySql.Data
,这就让人无语了,并且反馈都是升级了MySql.Data
驱动引发,接下来我们简单聊一下。
mysqldata-到底怎么了">二: MySql.Data 到底怎么了
1. 祸根溯源
早期版本的 MySql.Data
访问数据库都是以同步的方式进行,比如:ExecuteReader
而不是 ExecuteReaderAsync
,随着项目的升级改造需要提升MySql.Data的版本, MySql为了向前兼容保留了同步方法,下面引用最新的 MySql.Data 9.1.0 截图和参考代码如下:
// MySql.Data, Version=9.1.0.0, Culture=neutral, PublicKeyToken=c5687fc88969c44d
// MySql.Data.MySqlClient.MySqlConnection
using System.Threading;public override void Open()
{OpenAsync(execAsync: false, CancellationToken.None).GetAwaiter().GetResult();
}// MySql.Data, Version=9.1.0.0, Culture=neutral, PublicKeyToken=c5687fc88969c44d
// MySql.Data.MySqlClient.MySqlCommand
using System.Data;
using System.Threading;public new MySqlDataReader ExecuteReader()
{return ExecuteReaderAsync(CommandBehavior.Default, execAsync: false, CancellationToken.None).GetAwaiter().GetResult();
}public override object ExecuteScalar()
{return ExecuteScalarAsync(execAsync: false, CancellationToken.None).GetAwaiter().GetResult();
}
仔细看上面这段代码,不觉让人吸了一口凉气,所谓的同步方式竟然是用异步方法简单包装
而来的,这种异步混用同步的方式很容易导致线程饥饿,即线程池中已无可用线程来唤醒 GetResult() 下的 Event 事件,这个我准备后面用一篇文章详细来聊一下线程饥饿,这里用C#内功修炼训练营
中的一张图来演示下.NET8 中异步在线程池中的走法。