IEnumerator SignalBasedAdvancedMultithreadYielding()
        {
            var bounds = new Bounds(_BoundCenter, _BoundSize);

            //these will help with synchronization between threads
            WaitForSignalEnumerator mainWaitForSignal  = new WaitForSignalEnumerator("MainThreadWait", 1000);
            WaitForSignalEnumerator otherwaitForSignal = new WaitForSignalEnumerator
                                                             ("OtherThreadWait", () => isActiveAndEnabled == false, 1000);

            //Start the operations on other threads
            OperationsRunningOnOtherThreads(mainWaitForSignal, otherwaitForSignal)
            .RunOnScheduler(StandardSchedulers.multiThreadScheduler);

            //start the main thread loop
            while (true)
            {
                _time = Time.time / 10;

                //Since we want to feed the GPU with the data processed
                //from the other thread, we can't set the particleDataBuffer
                //until this operation is done. For this reason we stall
                //the mainthread until the data is ready. This operation is advanced
                //as it could stall the game for ever if you don't know
                //what you are doing!
                otherwaitForSignal.Complete();
#if BENCHMARK
                if (PerformanceCheker.PerformanceProfiler.showingFPSValue > 30.0f)
                {
                    if (_pc.particlesLimit >= 16)
                    {
                        _pc.particlesLimit -= 16;
                    }

                    PerformanceCheker.PerformanceProfiler.particlesCount = _pc.particlesTransformed;
                }
#endif
                _pc.particlesTransformed = 0;
                _particleDataBuffer.SetData(_gpuparticleDataArr);

                //tell to the other thread that now it can perform the operations
                //for the next frame.
                mainWaitForSignal.Signal();

                //do something seriously slow
#if DO_SOMETHING_SERIOUSLY_SLOW
                Thread.Sleep(10);
#endif
                //render the particles. I use DrawMeshInstancedIndirect but
                //there aren't any compute shaders running. This is so cool!
                Graphics.DrawMeshInstancedIndirect(_pointMesh, 0, _material,
                                                   bounds, _GPUInstancingArgsBuffer);

                //continue the cycle on the next frame
                yield return(null);
            }
        }
        IEnumerator MainThreadOperations(ParticleCounter particleCounter)
        {
            var bounds = new Bounds(_BoundCenter, _BoundSize);

            var syncRunner = new SyncRunner();

            //these will help with synchronization between threads
            WaitForSignalEnumerator _waitForSignal      = new WaitForSignalEnumerator(() => _breakIt);
            WaitForSignalEnumerator _otherwaitForSignal = new WaitForSignalEnumerator();

            //Start the operations on other threads
            OperationsRunningOnOtherThreads(_waitForSignal, _otherwaitForSignal)
            .ThreadSafeRunOnSchedule(StandardSchedulers.multiThreadScheduler);

            //start the mainloop
            while (true)
            {
                _time = Time.time / 10;

                //wait until the other thread tell us that the data is ready to be used.
                //Note that I am stalling the main thread here! This is entirely up to you
                //if you don't want to stall it, as you can see with the other use cases
                _otherwaitForSignal.RunOnSchedule(syncRunner);
#if BENCHMARK
                if (PerformanceCheker.PerformanceProfiler.showingFPSValue > 30.0f)
                {
                    if (particleCounter.particlesLimit >= 16)
                    {
                        particleCounter.particlesLimit -= 16;
                    }

                    PerformanceCheker.PerformanceProfiler.particlesCount = particleCounter.particlesTransformed;
                }

                particleCounter.particlesTransformed = 0;
#endif

                _particleDataBuffer.SetData(_gpuparticleDataArr);

                //render the particles. I use DrawMeshInstancedIndirect but
                //there aren't any compute shaders running. This is so cool!
                Graphics.DrawMeshInstancedIndirect(_pointMesh, 0, _material,
                                                   bounds, _GPUInstancingArgsBuffer);

                //tell to the other thread that now it can perform the operations
                //for the next frame.
                _waitForSignal.Signal();

                //continue the cycle on the next frame
                yield return(null);
            }
        }
        IEnumerator OperationsRunningOnOtherThreads(WaitForSignalEnumerator waitForSignal,
                                                    WaitForSignalEnumerator otherWaitForSignal)
        {
            //a SyncRunner stop the execution of the thread until the task is not completed
            //the parameter true means that the runner will sleep in between yields
            var syncRunner = new SyncRunner();

            while (_breakIt == false)
            {
                //execute the tasks. The MultiParallelTask is a special collection
                //that uses N threads on its own to execute the tasks. This thread
                //doesn't need to do anything else meanwhile and will yield until
                //is done. That's why the syncrunner can sleep between yields, so
                //that this thread won't take much CPU just to wait the parallel
                //tasks to finish
                _multiParallelTasks.ThreadSafeRunOnSchedule(syncRunner);
#if BENCHMARK //do it x4
                _multiParallelTasks.ThreadSafeRunOnSchedule(syncRunner);
                _multiParallelTasks.ThreadSafeRunOnSchedule(syncRunner);
                _multiParallelTasks.ThreadSafeRunOnSchedule(syncRunner);
#endif
                //the 1 Million particles operation are done, let's signal that the
                //result can now be used
                otherWaitForSignal.Signal();
                //wait until the application is over or the main thread will tell
                //us that now we can perform again the particles operation. This
                //is an explicit while instead of a yield, just because if the _breakIt
                //condition, which is needed only because if this application runs
                //in the editor, the threads spawned will not stop until the Editor is
                //shut down.
                waitForSignal.RunOnSchedule(syncRunner);
            }

            //the application is shutting down. This is not that necessary in a
            //standalone client, but necessary to stop the thread when the
            //application is stopped in the Editor to stop all the threads.
            _multiParallelTasks.ClearAndKill();

            TaskRunner.Instance.StopAndCleanupAllDefaultSchedulerTasks();

            yield break;
        }
        IEnumerator OperationsRunningOnOtherThreads(WaitForSignalEnumerator mainWaitForSignal,
                                                    WaitForSignalEnumerator otherWaitForSignal)
        {
            while (true)
            {
                //execute the tasks. The MultiParallelTask is a special collection
                //that uses N threads on its own to execute the tasks. The
                //complete operation is similar to the Unity Jobs complete
                //operations. It stalls the thread where it's called from
                //until everything is done!
                yield return(_multiParallelTasks);

                //the 1 Million particles operation are done, let's signal that the
                //result can now be used
                otherWaitForSignal.Signal();
                //yield until the application is over or the main thread will tell
                //us that now we can perform again the particles operation.
                //since we are not using the thread for anything else
                //we can stall the thread here until is done
                yield return(mainWaitForSignal);
            }
        }