Threadsafe FIFO Queue/Buffer

别说谁变了你拦得住时间么 提交于 2019-12-30 06:18:49

问题


I need to implement a sort of task buffer. Basic requirements are:

  • Process tasks in a single background thread
  • Receive tasks from multiple threads
  • Process ALL received tasks i.e. make sure buffer is drained of buffered tasks after a stop signal is received
  • Order of tasks received per thread must be maintained

I was thinking of implementing it using a Queue like below. Would appreciate feedback on the implementation. Are there any other brighter ideas to implement such a thing?

public class TestBuffer
{
    private readonly object queueLock = new object();
    private Queue<Task> queue = new Queue<Task>();
    private bool running = false;

    public TestBuffer()
    {
    }

    public void start()
    {
        Thread t = new Thread(new ThreadStart(run));
        t.Start();
    }

    private void run()
    {
        running = true;

        bool run = true;
        while(run)
        {
            Task task = null;
            // Lock queue before doing anything
            lock (queueLock)
            {
                // If the queue is currently empty and it is still running
                // we need to wait until we're told something changed
                if (queue.Count == 0 && running)
                {
                    Monitor.Wait(queueLock);
                }

                // Check there is something in the queue
                // Note - there might not be anything in the queue if we were waiting for something to change and the queue was stopped
                if (queue.Count > 0)
                {
                    task = queue.Dequeue();
                }
            }

            // If something was dequeued, handle it
            if (task != null)
            {
                handle(task);
            }

            // Lock the queue again and check whether we need to run again
            // Note - Make sure we drain the queue even if we are told to stop before it is emtpy
            lock (queueLock)
            {
                run = queue.Count > 0 || running;
            }
        }
    }

    public void enqueue(Task toEnqueue)
    {
        lock (queueLock)
        {
            queue.Enqueue(toEnqueue);
            Monitor.PulseAll(queueLock);
        }
    }

    public void stop()
    {
        lock (queueLock)
        {
            running = false;
            Monitor.PulseAll(queueLock);
        }
    }

    public void handle(Task dequeued)
    {
        dequeued.execute();
    }
}

回答1:


You can actually handle this with the out-of-the-box BlockingCollection.

It is designed to have 1 or more producers, and 1 or more consumers. In your case, you would have multiple producers and one consumer.

When you receive a stop signal, have that signal handler

  • Signal producer threads to stop
  • Call CompleteAdding on the BlockingCollection instance

The consumer thread will continue to run until all queued items are removed and processed, then it will encounter the condition that the BlockingCollection is complete. When the thread encounters that condition, it just exits.




回答2:


You should think about ConcurrentQueue, which is FIFO, in fact. If not suitable, try some of its relatives in Thread-Safe Collections. By using these you can avoid some risks.




回答3:


I suggest you take a look at TPL DataFlow. BufferBlock is what you're looking for, but it offers so much more.




回答4:


Look at my lightweight implementation of threadsafe FIFO queue, it doesn't block anything and uses threadpool - better then create own threads in most cases. https://github.com/Gentlee/SerialQueue

Usage:

var queue = new SerialQueue();
var result = await queue.Enqueue(LongRunningWork);



回答5:


You could use Rx on .NET 3.5 for this. It might have never come out of RC, but I believe it is stable* and in use by many production systems. If you don't need Subject you might find primitives (like concurrent collections) for .NET 3.5 you can use that didn't ship with the .NET Framework until 4.0.

Alternative to Rx (Reactive Extensions) for .net 3.5

* - Nit picker's corner: Except for maybe advanced time windowing, which is out of scope, but buffers (by count and time), ordering, and schedulers are all stable.



来源:https://stackoverflow.com/questions/12375339/threadsafe-fifo-queue-buffer

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!