Beispiel #1
0
            public void ShouldRunThreadsWithDistinctKeysInParallel()
            {
                // Arrange
                var currentParallelism = 0;
                var maxParallelism     = 0;
                var parallelismLock    = new object();

                // 100 threads, 100 keys
                var threads = Enumerable.Range(0, 100)
                              .Select(i => new Thread(() => OccupyTheLockALittleBit(i)))
                              .ToList();

                // Act
                foreach (var thread in threads)
                {
                    thread.Start();
                }

                foreach (var thread in threads)
                {
                    thread.Join();
                }

                maxParallelism.Should().BeGreaterThan(10);

                void OccupyTheLockALittleBit(int key)
                {
                    using (KeyedSemaphore.Lock(key.ToString()))
                    {
                        var incrementedCurrentParallelism = Interlocked.Increment(ref currentParallelism);

                        lock (parallelismLock)
                        {
                            maxParallelism = Math.Max(incrementedCurrentParallelism, maxParallelism);
                        }

                        const int delay = 250;

                        Thread.Sleep(TimeSpan.FromMilliseconds(delay));

                        Interlocked.Decrement(ref currentParallelism);
                    }
                }
            }
Beispiel #2
0
            public void ShouldRunThreadsWithSameKeysLinearly()
            {
                // Arrange
                var runningThreadsIndex = new ConcurrentDictionary <int, int>();
                var parallelismLock     = new object();
                var currentParallelism  = 0;
                var maxParallelism      = 0;

                // 100 threads, 10 keys
                var threads = Enumerable.Range(0, 100)
                              .Select(i => new Thread(() => OccupyTheLockALittleBit(i % 10)))
                              .ToList();

                // Act
                foreach (var thread in threads)
                {
                    thread.Start();
                }

                foreach (var thread in threads)
                {
                    thread.Join();
                }

                // Assert
                maxParallelism.Should().BeLessOrEqualTo(10);

                void OccupyTheLockALittleBit(int key)
                {
                    using (KeyedSemaphore.Lock(key.ToString()))
                    {
                        var incrementedCurrentParallelism = Interlocked.Increment(ref currentParallelism);

                        lock (parallelismLock)
                        {
                            maxParallelism = Math.Max(incrementedCurrentParallelism, maxParallelism);
                        }

                        var currentThreadId = Thread.CurrentThread.ManagedThreadId;
                        if (runningThreadsIndex.TryGetValue(key, out var otherThread))
                        {
                            throw new Exception($"Thread #{currentThreadId} acquired a lock using key ${key} " +
                                                $"but another thread #{otherThread} is also still running using this key!");
                        }

                        runningThreadsIndex[key] = currentThreadId;

                        const int delay = 10;

                        Thread.Sleep(TimeSpan.FromMilliseconds(delay));

                        if (!runningThreadsIndex.TryRemove(key, out var value))
                        {
                            var ex = new Exception($"Thread #{currentThreadId} has finished " +
                                                   $"but when trying to cleanup the running threads index, the value is already gone");

                            throw ex;
                        }

                        if (value != currentThreadId)
                        {
                            var ex = new Exception($"Thread #{currentThreadId} has finished and has removed itself from the running threads index," +
                                                   $" but that index contained an incorrect value: #{value}!");

                            throw ex;
                        }

                        Interlocked.Decrement(ref currentParallelism);
                    }
                }
            }