Skip to content

Volatile vs Interlocked vs lock in C#: .NET 10 and a harness that races - #2276

Merged
vladimir-pecanac-main merged 3 commits into
CodeMazeBlog:mainfrom
vladimir-pecanac-main:seo/96092-csharp-volatile-interlocked-lock
Oct 15, 2026
Merged

vladimir-pecanac-main merged 3 commits into
CodeMazeBlog:mainfrom
vladimir-pecanac-main:seo/96092-csharp-volatile-interlocked-lock

Conversation

@vladimir-pecanac-main

Copy link
Copy Markdown
Collaborator

Updates the sample for the article "Volatile vs Interlocked vs lock in C#" (https://code-maze.com/csharp-volatile-interlocked-lock/). Only csharp-advanced-topics/VolatileInterlockedLockInCsharp changes.

  • Both projects move from net7.0 to net10.0.
  • Packages, re-queried on NuGet on 2026-10-10 (latest stable): Microsoft.NET.Test.Sdk 18.10.1, xunit 2.9.3, xunit.runner.visualstudio 4.0.1, coverlet.collector 10.1.0. The folder keeps xunit.
  • AccountService.WithdrawBalance now runs ten workers doing one hundred withdrawals each (the same 1,000 withdrawals of 100 from 100,000) instead of 1,000 one-shot tasks that each slept a random 50 to 300 ms. The sleep spread the tasks out so far that the race rarely happened and the unsynchronized and volatile runs often printed the correct 0. On the new harness, ten consecutive runs on .NET 10.0.10 never printed 0 unsynchronized or under volatile, and lock and Interlocked printed 0 in all ten. The test suite drops from about 40 seconds to well under a second.
  • private object _lock = new object(); becomes private readonly Lock _lock = new(); (System.Threading.Lock, .NET 9 and later).
  • New Account.WithdrawIfSufficient (an Interlocked.CompareExchange retry loop that never overdraws) and a new OneShotInitializer (Interlocked.Exchange as a one-shot gate), both demonstrated in Program.cs and covered by a test.
  • The two race tests asserted NotEqual(100000, ...) and NotEqual(1000, ...), which can never fail. Asserting that the race happened would be flaky, because a race is not guaranteed to occur, so they are renamed to ...ThenFinalBalanceIsNotGuaranteed and assert the invariant that always holds: the balance stays between 0 and 100,000.

Build: 0 warnings, 0 errors. Tests: 6 of 6 passing. dotnet list package --vulnerable --include-transitive: no vulnerable packages. SDK 10.0.302, runtime 10.0.10.

@vladimir-pecanac-main
vladimir-pecanac-main merged commit 5416e27 into CodeMazeBlog:main Oct 15, 2026
4 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant