Helper class to run a set of tasks in parallel. This class uses a number of worker threads witch will execute the queued tasks in parallel as much as possible. The worker threads are background threads and you must call Dispose() or Finish() to ensure all the tasks are finished
Наследование: IDisposable
        public void ParallelRunnerFlushTasksTest()
        {
            for (int count = 0; count < 20; count++)
            {
                using (TaskExecutor runner = new TaskExecutor())
                {
                    int i = 0;
                    double r;

                    foreach (var num in Enumerable.Range(0, count))
                    {
                        runner.AddTask(() =>
                        {
                            for (int j = 0; j < 1000; j++)
                            {
                                r = Math.Sin(0.234234) * Math.Atan(j);
                            }
                            Interlocked.Increment(ref i);
                        });
                    }

                    (i <= count).Should().BeTrue();

                    runner.FlushTasks();

                    i.Should().Be(count);

                    runner.Finish();

                    i.Should().Be(count);
                }
            }
        }
        public void QueuedTaskRunnerErrorTest()
        {
            Action action = () => { throw new InvalidOperationException("boom"); };
            int calledTimes = 0;
            int baseThreadId = System.Threading.Thread.CurrentThread.ManagedThreadId;
            using (TaskExecutor runner = new TaskExecutor(1))
            {
                runner.HasErrors.Should().BeFalse();
                runner.OnTaskError += (sender, args) =>
                {
                    sender.Should().BeSameAs(runner);
                    args.TaskException.Should().BeOfType<InvalidOperationException>();
                    System.Threading.Thread.CurrentThread.ManagedThreadId.Should().Be(baseThreadId);
                    calledTimes++;
                    args.WasHandled = true;
                };
                runner.OnThreadTaskError += (sender, args) =>
                {
                    sender.Should().BeSameAs(runner);
                    args.TaskException.Should().BeOfType<InvalidOperationException>();
                    baseThreadId.Should().NotBe(System.Threading.Thread.CurrentThread.ManagedThreadId);

                    args.WasHandled = true;
                };

                foreach (var n in Enumerable.Range(0, 5))
                {
                    runner.AddTask(action);
                }
                runner.Finish();
                runner.HasErrors.Should().BeTrue();
            }
            calledTimes.Should().Be(5);
        }
        public void ParallelRunnerTestTaskCount()
        {
            using (TaskExecutor runner = new TaskExecutor())
            {
                const int count = 10;

                foreach (var num in Enumerable.Range(0, count))
                {
                    runner.AddTask(() => Thread.Sleep(100));
                }

                runner.TotalAddedTasks.Should().Be(count);
                runner.RemainingTasks.Should().Be(count);
                runner.ErrorCount.Should().Be(0);
                runner.FinishedTasksCount.Should().Be(0);

                runner.FlushTasks();

                runner.TotalAddedTasks.Should().Be(count);
                runner.RemainingTasks.Should().Be(0);
                runner.FinishedTasksCount.Should().Be(count);
                runner.ErrorCount.Should().Be(0);

                runner.Finish();

                runner.TotalAddedTasks.Should().Be(count);
                runner.RemainingTasks.Should().Be(0);
                runner.FinishedTasksCount.Should().Be(count);
                runner.ErrorCount.Should().Be(0);
            }
        }
        public void QueuedTaskRunnerRunTaskTest()
        {
            const int nrTask = 100;
            int count = 0;

            using (TaskExecutor runner = new TaskExecutor(1))
            {
                for (int i = 0; i < nrTask; ++i)
                {
                    runner.AddTask(() => count++);
                }
            }

            count.Should().Be(nrTask);
        }
 public void QueuedTaskRunnerWorkerThreadTest()
 {
     TaskExecutor runner = new TaskExecutor(1);
     bool hasThrown = false;
     try
     {
         runner.Finish();
         runner.Finish();
         runner.Dispose();
     }
     catch
     {
         hasThrown = true;
     }
     hasThrown.Should().BeFalse();
 }
        public void ParallelRunnerTest()
        {
            const int task = 100;
            int sum = 0;

            using (TaskExecutor runner = new TaskExecutor())
            {
                object pad = new object();
                foreach (var num in Enumerable.Range(0, task))
                {
                    runner.AddTask(() =>
                    {
                        lock (pad) { sum++; }
                    });
                }
                runner.Finish();
            }
            sum.Should().Be(task);
        }
        public void ParallelRunnerTestMultipleThreads()
        {
            using (TaskExecutor runner = new TaskExecutor(5))
            {

                int times = 1000;
                int taskCount = 10000;
                int currentTask = 0;
                int val = 0;

                Action task = null;
                task = () =>
                {
                    Interlocked.Increment(ref currentTask);
                    Interlocked.Increment(ref val);
                    if (currentTask <= taskCount)
                        runner.AddTask(task);
                    Thread.SpinWait((taskCount % 30) * 10000);
                };


                Enumerable.Range(0, times).ToList().ForEach(n => runner.AddTask(task));

                runner.FlushTasks();

                val.Should().Be(taskCount + times);
            }
        }
        public void ParallelRunnerStressTest()
        {
            // 1000 increments with 2ms sleep each on 2 threads
            // 1000 * 2ms / 2threads = 1000ms min wait

            using (TaskExecutor runner = new TaskExecutor(2))
            {

                int val = 0;

                Enumerable.Range(0, 1000).ToList().ForEach(n => runner.AddTask(() => { Thread.Sleep(2); Interlocked.Increment(ref val); }));

                runner.FlushTasks();

                val.Should().Be(1000);
                runner.Finish();
            }
        }
        public void ParallelRunnerHandeledExceptionTest()
        {
            using (TaskExecutor runner = new TaskExecutor())
            {
                runner.OnTaskError += (s, e) => e.WasHandled = true;

                InvalidOperationException x = new InvalidOperationException();

                runner.AddTask(() => { throw x; });

                Action action = () => runner.Finish();

                action.ShouldNotThrow();
            }
        }
        public void ParallelRunnerExceptionTest()
        {
            using (TaskExecutor runner = new TaskExecutor())
            {
                InvalidOperationException x = new InvalidOperationException();

                runner.AddTask(() => { throw x; });

                AggregateException pex = null;

                try
                {
                    runner.Finish();
                }
                catch (AggregateException ex)
                {
                    pex = ex;
                }

                pex.InnerExceptions.Single().Should().Be(x);
            }
        }
        public void ParallelRunnerFlushTestThreadSleep()
        {
            using (TaskExecutor runner = new TaskExecutor())
            {
                int i = 0;

                foreach (var n in Enumerable.Range(0, 10))
                {
                    runner.AddTask(() =>
                    {
                        if (n % 2 == 0)
                        {
                            Thread.Sleep(100);
                        }
                        Interlocked.Increment(ref i);
                    });
                }

                (i <= 10).Should().BeTrue();

                runner.FlushTasks();

                i.Should().Be(10);

                runner.Finish();

                i.Should().Be(10);
            }
        }
        public void ParallelRunnerFlushTestAutoThreads()
        {
            using (TaskExecutor runner = new TaskExecutor())
            {
                int i = 0;

                const int count = 10;

                foreach (var n in Enumerable.Range(0, count))
                {
                    runner.AddTask(() =>
                    {
                        if (n % 2 == 0)
                        {
                            Thread.SpinWait(int.MaxValue / 200);
                        }
                        Interlocked.Increment(ref i);
                    });
                }

                (i <= count).Should().BeTrue();

                runner.FlushTasks();

                i.Should().Be(count);

                runner.Finish();

                i.Should().Be(count);
            }
        }