MySql.Data 与 'new Thread' 对比 ThreadPool:使用 ThreadPool 会造成死锁

MySql.Data with 'new Thread' vs ThreadPool: using ThreadPool creates a deadlock

在一个项目中,我发现了一个奇怪的行为,然后使用 ThreadPool 中的线程连接到 MySQL 实例;应用程序冻结,看起来是由于 MySqlConnection.Open 内部的死锁。当我手动创建 Thread 和 运行 Start 方法时,不会发生这种情况。

This project on GitHub 展示了这一点,我想帮助理解为什么它会这样。

这是来自 运行ning TestTask 的屏幕截图:

所有线程都在MySqlConnection.Open方法中,并停留在那里。他们似乎没有进一步打印出一些日志消息 - 没有为我生成任何日志消息 运行ning TestTask.

另一方面,运行ning TestThreads 有效,然后我得到了我期望的日志消息:

我想了解为什么在创建手动线程时 ThreadPool 会失败?为什么 MySqlConnection 在一种情况下会死锁,而在另一种情况下不会?

请注意,由于我们是针对同一个 MySQL 服务器进行测试,因此这不是 MySQL 配置问题。我也不明白 ThreadPool 饥饿是怎么回事;我们将 MinThread 设置为 200。此外,由于在 TestTask 的情况下我从来没有得到一个 Console.WriteLine,所以肯定还有其他东西吗?

MySql.Data 里面有一些写得不好的异步代码(除了 之外 Async 方法的 none 实际上是异步的)。

例如,此代码使用 Task 开始异步工作,然后调用 Task.Waithttps://github.com/mysql/mysql-connector-net/blob/4306c8484ec74b3ee1c349847f66acabbce6d63c/MySQL.Data/src/common/StreamCreator.cs#L98-L100

这是“sync-over-async”,即well-known cause of deadlocks。问题很可能是线程池被您的用户代码阻塞了,所以完成 TcpClient.ConnectAsync 所需的工作无法排队,所以 Task 中的 none 可以完成,所以没有代码可以向前推进。

你可以通过强制线程池有更多线程来解决这个问题(这在我的机器上有效,但它可能不是一个有保证的解决方案):

ThreadPool.SetMinThreads(500, 500);
// do NOT call ThreadPool.SetMaxThreads

您也可以切换到MySqlConnector, which implements threading code properly, and doesn't need the SetMinThreads workaround in order to run. I've added an example of what using MySqlConnector's async methods would be like。 (披露:我是 MySqlConnector 的首席开发人员。)