Home > Software design >  MySql.Data with 'new Thread' vs ThreadPool: using ThreadPool creates a deadlock
MySql.Data with 'new Thread' vs ThreadPool: using ThreadPool creates a deadlock

Time:11-14

In a project, I discovered a peculiar behavior then connecting to a MySql instance using threads from a ThreadPool; the application froze and it looks like it is due to a deadlock inside MySqlConnection.Open. This does not happen when I manually create the Thread and run the Start method.

enter image description here

All the threads are in the MySqlConnection.Open method, and stays there. They don't seem to get any further to print out some log messsages - no log messages are ever produced for me running TestTask.

On the other hand, running TestThreads works, then then I get the log message I expect:

enter image description here

I would like to understand why ThreadPool fails when creating manual threads works? Why is MySqlConnection deadlocked in one case, but not the other?

Please note that since we are testing against the same MySql server, it is not a MySql configuration issue. I also dont see how it can be ThreadPool starvation; we set the MinThread to 200. Also, since I never get a single Console.WriteLine in the TestTask case, there must surely be something else?

CodePudding user response:

MySql.Data has some poorly-written async code inside it (besides the problem that none of the Async methods are actually async).

For example, this code starts async work using a Task, then calls Task.Wait: https://github.com/mysql/mysql-connector-net/blob/4306c8484ec74b3ee1c349847f66acabbce6d63c/MySQL.Data/src/common/StreamCreator.cs#L98-L100

This is "sync-over-async", which is a well-known cause of deadlocks. The problem is most likely that the threadpool is blocked with your user code, so the work required to complete TcpClient.ConnectAsync can't be queued, so none of the Tasks can complete, so no code can make forward progress.

You can work around that by forcing the threadpool to have more threads:

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

You can also switch to 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.

  • Related