public async Task StressTaskScheduling()
        {
            var logger        = new CountTasksLogger();
            var taskScheduler = new MultiThreadTaskScheduler(logger);

            taskScheduler.SetSchedulingOptions(new MultiThreadSchedulingSettings
            {
                Mode           = MultiThreadCreationMode.OneThreadPerCpuCore,
                ThreadPriority = System.Threading.ThreadPriority.BelowNormal
            });

            var numThreads = taskScheduler.SchedulingOptions.NumberOfThreads;

            var tasks         = new Task[numThreads * 50];
            int overflowCount = 0;

            // Deliberately make the work unbalanced
            int GetNumChildTasks(int i) => 1000 + (i * 800) / numThreads;

            int totalChildTasks = Enumerable.Range(0, tasks.Length).Select(GetNumChildTasks).Sum();

            for (int i = 0; i < tasks.Length; ++i)
            {
                var iCopy = i;

                // This task goes to the global queue
                tasks[i] = TaskExtensions.Unwrap(Task.Factory.StartNew(async() =>
                {
                    var subtasks = new Task <(int, int)> [GetNumChildTasks(iCopy)];
        public async Task LocallyQueuedTasks()
        {
            var logger        = new CountTasksLogger();
            var taskScheduler = new MultiThreadTaskScheduler(logger);

            taskScheduler.SetSchedulingOptions(new MultiThreadSchedulingSettings
            {
                NumberOfThreads = 4,
                ThreadPriority  = System.Threading.ThreadPriority.BelowNormal
            });

            var       tasks            = new Task[6];
            int       stolenTasksCount = 0;
            const int numChildTasks    = 50;

            for (int i = 0; i < tasks.Length; ++i)
            {
                var iCopy = i;

                tasks[i] = TaskExtensions.Unwrap(Task.Factory.StartNew(async() =>
                {
                    var random   = new Random();
                    var subtasks = new Task[numChildTasks];
                    var threadId = Environment.CurrentManagedThreadId;

                    for (int j = 0; j < subtasks.Length; ++j)
                    {
                        var jCopy   = j;
                        var delayMs = random.Next(10, 80);

                        // This child task goes to the local queue
                        subtasks[j] = Task.Factory.StartNew(() =>
                        {
                            if (Environment.CurrentManagedThreadId != threadId)
                            {
                                Interlocked.Increment(ref stolenTasksCount);
                            }

                            // Pretend to be doing work.
                            Thread.Sleep(delayMs);
                            return(iCopy, jCopy);
                        });
                    }

                    await Task.WhenAll(subtasks);
                }, CancellationToken.None, TaskCreationOptions.None, taskScheduler));
            }

            await Task.WhenAll(tasks);

            Assert.Equal(stolenTasksCount, logger.StolenTasksCount);
            Assert.Equal(tasks.Length, logger.GlobalTasksCount);
            Assert.Equal(tasks.Length * numChildTasks - stolenTasksCount, logger.LocalTasksCount);

            await taskScheduler.DisposeAsync();
        }
        public async Task GloballyQueuedTasks()
        {
            var logger        = new CountTasksLogger();
            var taskScheduler = new MultiThreadTaskScheduler(logger);

            taskScheduler.SetSchedulingOptions(new MultiThreadSchedulingSettings
            {
                NumberOfThreads = 4,
                ThreadPriority  = System.Threading.ThreadPriority.BelowNormal
            });

            var random   = new Random();
            var subtasks = new Task <int> [300];

            for (int i = 0; i < subtasks.Length; ++i)
            {
                var delayMs = random.Next(10, 80);
                int iCopy   = i; // freeze for lambda capture
                subtasks[i] = Task.Factory.StartNew(() =>
                {
                    // Pretend to be doing work.
                    Thread.Sleep(delayMs);
                    return(iCopy);
                }, CancellationToken.None, TaskCreationOptions.None, taskScheduler);
            }

            await Task.WhenAll(subtasks);

            for (int i = 0; i < subtasks.Length; ++i)
            {
                Assert.Equal(i, subtasks[i].Result);
            }

            Assert.Equal(subtasks.Length, logger.GlobalTasksCount);

            await taskScheduler.DisposeAsync();

            Assert.InRange(logger.WorkersCount, 1, taskScheduler.SchedulingOptions.NumberOfThreads);
            Assert.Equal(logger.WorkersStopped, logger.WorkersCount);
        }