Demonstrating that single-threaded, lock-free architectures can outperform traditional multi-threaded approaches
A comprehensive C# demonstration project showcasing various concurrency patterns and their performance characteristics, from traditional database locking to high-performance lock-free architectures inspired by the LMAX Disruptor pattern.
- Overview
- Project Structure
- Demos
- Prerequisites
- Getting Started
- Key Concepts
- Performance Targets
- Technologies Used
- License
This project demonstrates the evolution from traditional locking mechanisms to high-performance single-threaded processing using a ticket reservation system as the domain model. Each demo progressively removes layers of locking and overhead, culminating in a mechanical sympathy-focused architecture that achieves 10x-50x performance improvements.
- Demo 1: SQL Database Locking (100-500 TPS) - Traditional ACID with pessimistic/optimistic locking
- Demo 2: In-Memory Locks (1,000-3,000 TPS) - Remove database overhead, expose lock contention
- Demo 3: Single-Threaded (10,000-50,000+ TPS) - Lock-free ring buffer with mechanical sympathy
single-threaded-power/
├── src/
│ ├── single-threaded-power.sln # Solution file
│ ├── SingleThreadedPower.Domain/ # Shared domain models & interfaces
│ ├── SingleThreadedPower.Demo1.SqlLocking/ # Database locking demo
│ ├── SingleThreadedPower.Demo2.InMemoryLocks/ # In-memory locking patterns
│ ├── SingleThreadedPower.Demo3.SingleThreaded/ # Lock-free Disruptor pattern
│ ├── SingleThreadedPower.LoadGenerator/ # Performance testing framework
│ ├── SingleThreadedPower.Console/ # Interactive demo runner
│ └── SingleThreadedPower.Tests/ # Unit & benchmark tests
└── docs/ # Additional documentation
Demonstrates traditional database concurrency control with Entity Framework Core 8.
Features:
- ✅ Pessimistic Locking (SELECT FOR UPDATE with UPDLOCK, ROWLOCK hints)
- ✅ Optimistic Locking (version/timestamp-based)
- ✅ Deadlock scenarios and handling
- ✅ Connection pooling configuration
- ✅ Lock wait time logging with interceptors
- ✅ Testcontainers for isolated MS SQL Server instances
Expected Performance: 100-500 TPS
Eliminates database overhead to demonstrate that locks are still a bottleneck.
Implementations:
- LockBasedReservationService: Traditional
lockstatements with global, per-event, and per-seat strategies - SemaphoreReservationService:
SemaphoreSlimfor throttling with fairness problem demonstrations - ReaderWriterReservationService:
ReaderWriterLockSlimfor read-heavy workloads - CSharp13LockReservationService: Using new
System.Threading.Lock(C# 13/.NET 9)
Metrics:
- Lock contention tracking
- Context switching overhead
- CPU utilization analysis
Expected Performance: 1,000-3,000 TPS
High-performance single-threaded processing with mechanical sympathy.
Architecture:
- DisruptorReservationService: Lock-free ring buffer with single consumer thread
- RingBuffer: Pre-allocated, power-of-2 sized, cache-line padded array
- ReservationCommand: Value-type command pattern (
readonly record struct) - TaskCompletionSource: Async responses without blocking
Modern C# Features:
ref structfor zero-allocationSpan<T>andMemory<T>for efficient memory accessValueTask<T>for reduced allocations[SkipLocalsInit]for performance- Static abstract interface members
Expected Performance: 10,000-50,000+ TPS with p99 latency < 1ms
- .NET 8 SDK
- Docker Desktop (for Demo 1 with Testcontainers)
- Optional: .NET 9 Preview (for C# 13 Lock demo)
git clone https://github.com/joeldickson/single-threaded-power.git
cd single-threaded-power# Build the solution
cd src
dotnet build single-threaded-power.sln -c Release
# Or build specific demo
dotnet build SingleThreadedPower.Demo3.SingleThreaded/SingleThreadedPower.Demo3.SingleThreaded.csproj -c Releasecd src/SingleThreadedPower.Console
dotnet runThe console provides an interactive menu to:
- Select which demo to run
- Configure event size, concurrent users, and test duration
- View real-time metrics with live updating dashboards
- Compare different approaches side-by-side
cd src
dotnet test SingleThreadedPower.Tests/SingleThreadedPower.Tests.csprojUnderstanding and working with the hardware (CPU cache lines, memory alignment, false sharing) rather than against it.
Techniques Used:
- Cache-line padding to prevent false sharing
- Power-of-2 sizing for efficient modulo operations
- Sequential memory access patterns
- Minimizing allocations in hot paths
Using atomic operations and memory barriers instead of traditional locks.
Benefits:
- No context switching overhead
- Predictable latency
- Better CPU cache utilization
- Scalability without lock contention
A high-performance inter-thread messaging library pattern created by LMAX Exchange.
Core Ideas:
- Pre-allocated ring buffer
- Single writer principle
- Batching for efficiency
- Minimizing write contention
| Demo | Throughput (TPS) | Avg Latency | P99 Latency | CPU Efficiency |
|---|---|---|---|---|
| SQL Locking | 100-500 | 10-50ms | 100-500ms | Low (I/O bound) |
| In-Memory Locks | 1,000-3,000 | 1-5ms | 10-50ms | Medium (contention) |
| Single-Threaded | 10,000-50,000+ | <0.5ms | <1ms | High (cache-friendly) |
- C# 12 / .NET 8: Primary language features (collection expressions, primary constructors)
- Entity Framework Core 8: Database access and locking demos
- Testcontainers: Disposable test infrastructure
- Spectre.Console: Beautiful terminal UI
- xUnit / BenchmarkDotNet: Testing and performance measurement
Contributions are welcome! Please feel free to submit a Pull Request. For major changes, please open an issue first to discuss what you would like to change.
This project is licensed under the MIT License - see the LICENSE file for details.
Joel Dickson
- GitHub: @joeldickson
Give a ⭐️ if this project helped you understand high-performance concurrency patterns!
Built with ❤️ to demonstrate that sometimes the best way to handle concurrency is to avoid it altogether.