I needed a very basic serial execution queue. I wrote the following based on this idea, but I needed a queue to ensure FIFO, so I added a intermediate ConcurrentQueue<>.
Here is the code:
public class SimpleSerialTaskQueue
{
    private SemaphoreSlim _semaphore = new SemaphoreSlim(0);
    private ConcurrentQueue<Func<Task>> _taskQueue = new ConcurrentQueue<Func<Task>>();
    public SimpleSerialTaskQueue()
    {
        Task.Run(async () =>
        {
            Func<Task> dequeuedTask;
            while (true)
            {
                if (await _semaphore.WaitAsync(1000))
                {
                    if (_taskQueue.TryDequeue(out dequeuedTask) == true)
                    {
                        await dequeuedTask();
                    }
                }
                else
                {
                    Console.WriteLine("Nothing more to process");
                    //If I don't do that , memory pressure is never released
                    //GC.Collect();
                }
            }
        });
    }
    public void Add(Func<Task> o_task)
    {
        _taskQueue.Enqueue(o_task);
        _semaphore.Release();
    }
}
When I run that in a loop, simulating heavy load, I get some kind of memory leak. Here is the code:
static void Main(string[] args)
{
    SimpleSerialTaskQueue queue = new SimpleSerialTaskQueue();
    for (int i = 0; i < 100000000; i++)
    {
        queue.Add(async () =>
        {
            await Task.Delay(0);
        });
    }
    Console.ReadLine();
}
EDIT: I don't understand why once the tasks have been executed, I still get like 750MB of memory used (based on VS2015 diagnostic tools). I thought once executed it would be very low. The GC doesnt seem to collect anything. Can anyone tell me what is happening? Is this related to the state machine