public async Task InvokeAsync(Action action, DispatcherPriority priority = DispatcherPriority.Normal, CancellationToken ct = default)
        {
            ThrowOnInvokeIfStateIsInvalid();

            var workItem = new DispatcherItem(action);

            _queues[(Int32)priority].Enqueue(workItem);
            _newWorkSemaphore.Release();
            var result = await workItem.WaitAsync(ct);

            if (result is Exception ex)
            {
                throw ex;
            }
        }
        public async Task <TResult> InvokeAsync <TResult>(Func <TResult> func, DispatcherPriority priority = DispatcherPriority.Normal, CancellationToken ct = default) where TResult : class
        {
            ThrowOnInvokeIfStateIsInvalid();

            var workItem = new DispatcherItem(func);

            _queues[(Int32)priority].Enqueue(workItem);
            _newWorkSemaphore.Release();

            var result = await workItem.WaitAsync(ct);

            if (result is Exception ex)
            {
                throw ex;
            }

            return((TResult)result);
        }
        public void Run()
        {
            State = DispatcherState.Running;

            while (true)
            {
                // Wait for new work or dispatcher shutting down
                _newWorkSemaphore.Wait();

                if (State == DispatcherState.ShuttingDown)
                {
                    foreach (var queue in _queues)
                    {
                        foreach (var item in queue)
                        {
                            item.SignalFinishedWithException(new OperationCanceledException());
                        }
                    }

                    break;
                }

                // Pick highest priority work
                DispatcherItem dispatcherItem = null;
                for (Int32 i = _queues.Length - 1; i >= 0; i--)
                {
                    _queues[i].TryDequeue(out dispatcherItem);
                    if (dispatcherItem != null)
                    {
                        break;
                    }
                }

                // This, actually, should never happen
                if (dispatcherItem == null)
                {
                    throw new DispatcherException("Work is null");
                }

                // Execute user action
                try
                {
                    if (dispatcherItem.DelegateWithoutResult != null)
                    {
                        dispatcherItem.DelegateWithoutResult.Invoke();
                        dispatcherItem.SignalFinished();
                    }
                    else
                    {
                        var result = dispatcherItem.DelegateWithResult.Invoke();
                        dispatcherItem.SignalFinished(result);
                    }
                }
                catch (Exception ex)
                {
                    dispatcherItem.SignalFinishedWithException(ex);
                }
            }

            _shutdownTaskCompletionSource.SetResult(null);
        }