Thread deadlock on TransactionScope.Dispose

Stefan Adriaenssen 86 Reputation points
2024-11-15T10:34:51.9033333+00:00

TransactionScopesTest.txt

Hello,

I'm experiencing thread deadlock issues when using the TransactionScope class in a .NET 8 project. The deadlock occurs on the TransactionScope.Dispose method. Strangely enough, this seems to be an issue only in .NET 8, but not in .NET Framework 4.8.

I have created a sample Visual Studio solution to illustrate this, which I added as attachment to this post. However, this website does not allow for zip files to be added, so I renamed to a TXT file. You'll have to rename to ZIP again in order to unzip it.

In this sample, a distributed transaction is needed to move some data from database A to database B (which requires DTC - it might even happen from server to server in the real world). The solution consists of:

  • 2 SQL scripts to create the sample databases (see folder 'SQLScripts')
  • A class library called 'Procedures', a .NET Standard 2.0 library that contains the actual code with the TransactionScope tests.
  • A console app called 'Runner-NET8', a .NET 8 console app for running the test in 'Procedures'
  • A console app called 'Runner-NETFW48', a .NET Framework 4.8 console app for running the test in 'Procedures'

Both console apps have a dependency to the 'Procedures' project, to ensure that the same code is called in both cases. The only external NuGet dependency is System.Data.SqlClient, version 4.9.0. At the time of writing, this is the latest version.

Procedure for testing:

  1. Make sure the databases exist, using the scripts
  2. Start either the .NET 8 or the .NET Framework 4.8 console app, make sure to start with debugger.
  3. If this is the first time you're running the app, press 'P' first to prepare the database (generates some dummy data in database A)
  4. Then choose to either run synchronously or asynchronously. For example, choose 'A' for asynchronous.
  5. I have written multiple implementations in an attempt to solve this issue. In the next menu you can choose which attempt you'd like to try. Doesn't matter which one you pick; they all fail in .NET 8, and they all succeed in .NET FW 4.8
  6. A loop will start running, which prints a single dot to the console every loop. Every loop moves 10 rows from A to B; removing them from A and inserting them into B.
  7. Wait for a while. In .NET 8, the dots will stop appearing after a short amount of time. When you notice this, press the pause button in Visual Studio ('break all').
  8. Notice that the debugger will stop at the Transaction.Dispose line, which is the finishing '}' for the using statement. The debugger detects a deadlock issue.
  9. When running asynchronously, you can press ENTER to close the app.

At random position, the .NET 8 version of the console app will hang. Sometimes immediately at the first cycle, sometimes after a couple of dozens of cycles. Notice that the .NET Framework 4.8 version of the console app doesn't experience such problems.

Even when running the fully synchronous sample (option 'S') in .NET 8 -which doesn't involve any threading at all- the deadlock issue occurs.

I have no idea what I'm doing wrong, so I hope someone can shed some light on this. Also, why any .Dispose method -from any class- would cause a deadlock is beyond me. I thought these methods were supposed to be safe to call at all times.

Kind regards,

Stefan Adriaenssen

.NET
.NET
Microsoft Technologies based on the .NET software framework.
3,981 questions
0 comments No comments
{count} votes

Accepted answer
  1. Viorel 118.5K Reputation points
    2024-11-15T21:20:44.0733333+00:00

    The Go function seems to work after adjusting the code to use the Microsoft.Data.SqlClient package instead of deprecated System.Data.SqlClient. Probably some things were improved and fixed.

    The other functions seem to work too.

    1 person found this answer helpful.

1 additional answer

Sort by: Most helpful
  1. Bruce (SqlWork.com) 68,311 Reputation points
    2024-11-15T18:17:51.98+00:00

    your link returns gibberish, and without sample code it hard to comment. but it appears you are getting deadlock trying to call async thread sync. the is a common issue:

    https://blog.stephencleary.com/2012/07/dont-block-on-async-code.html

    https://medium.com/rubrikkgroup/understanding-async-avoiding-deadlocks-e41f8f2c6f5d


Your answer

Answers can be marked as Accepted Answers by the question author, which helps users to know the answer solved the author's problem.