C#阻止收集生产者消费者而不阻止消费者线程

C#阻止收集生产者消费者而不阻止消费者线程,c#,.net,blockingcollection,C#,.net,Blockingcollection,我有一种情况,我需要有大量(数百)的队列,其中项目应该按顺序处理(需要单线程消费者)。在我的第一个实现中,我使用每个BlockingCollection一个长时间运行的任务来使用队列项。然而,由于队列大部分时间都是空的,因此我的应用程序中有数百个线程处于空闲状态,除了消耗内存之外什么也不做 我认为只有当队列中有东西要处理时,才让使用者任务运行会更好,但是,我还没有找到提供最佳实践的示例 我提出了一个类似于下面的解决方案。但问题是,每一项都会导致一项新任务(也许这是低效的?浪费资源?)。但是如果我

我有一种情况,我需要有大量(数百)的队列,其中项目应该按顺序处理(需要单线程消费者)。在我的第一个实现中,我使用每个BlockingCollection一个长时间运行的任务来使用队列项。然而,由于队列大部分时间都是空的,因此我的应用程序中有数百个线程处于空闲状态,除了消耗内存之外什么也不做

我认为只有当队列中有东西要处理时,才让使用者任务运行会更好,但是,我还没有找到提供最佳实践的示例

我提出了一个类似于下面的解决方案。但问题是,每一项都会导致一项新任务(也许这是低效的?浪费资源?)。但是如果我不为每个项目创建一个新任务,我就不能保证一个项目不会未经处理就出现在队列中

    private object _processSyncObj = new object();
    private volatile bool _isProcessing;
    private BlockingCollection<string> _queue = new BlockingCollection<string>();

    private void EnqueueItem(string item)
    {
        _queue.Add(item);
        Task.Factory.StartNew(ProcessQueue);
    }

    private void ProcessQueue()
    {
        if (_isProcessing)
            return;

        lock (_processSyncObj)
        {
             string item;
             while (_isProcessing = _queue.TryTake(out item))
             {
                 // process item
             }
        }
    }
private object\u processSyncObj=new object();
私有易失性bool\u处理;
private BlockingCollection_queue=new BlockingCollection();
私有void排队项(字符串项)
{
_添加(项目);
Task.Factory.StartNew(ProcessQueue);
}
私有void ProcessQueue()
{
如果(_i处理)
返回;
锁(_processSyncObj)
{
字符串项;
while(_isProcessing=_queue.TryTake(out项))
{
//过程项
}
}
}

在保证队列中没有项目,但没有消费者在运行的情况下,这种情况下的最佳做法/最佳解决方案是什么?

您考虑过吗?我相信您的场景可以很容易地满足。

我认为您所做的是合理的,因为该任务可以很好地扩展到数百万个任务,根据线程池生成内部子队列,避免了过多的上下文切换

在幕后,任务将排队到线程池,该线程池已通过确定和调整线程数量的算法得到增强,并提供负载平衡以最大限度地提高吞吐量。这使得任务相对轻量级,您可以创建许多任务以实现细粒度并行

…但是您所做的,最终将只是一个普通的任务编程,因为对于每个队列,您都会启动一个任务,因此阻塞集合是非常未使用的。据了解,您关心的是启动一个任务,并让TaskScheduler在作业到达时按顺序运行作业

您知道您还可以自定义任务调度器吗

使用一个任务编程模式,再加上一个定制的TaskScheduler来控制调度任务的流程,怎么样

例如,您可以创建一个OrderedTaskScheduler,它派生自一个LimitedConcurrentYlevelTaskScheduler,其行为如下

LimitedConcurrencyLevel TaskScheduler类提供了一个任务调度器,确保在线程池上运行时达到最大并发级别。必须设置此计划程序所需的最大并行度

类提供了一个任务调度器,确保一次只执行一个任务。任务按其排队顺序执行(FIFO)。它是LimitedConcurrentyLevel TaskScheduler的子类,它将1作为其基类构造函数的参数发送

您可以找到这些已经开发的调度程序,它们被称为,您可以从下载它,并阅读一些关于它的文章

您也可以直接在上找到它,并在上找到代码镜像


享受!:)
public class WorkerQueue<T>
{
    public WorkerQueue(Action<T> workerMethod)
    {
        _workerMethod = workerMethod;
        Task.Factory.StartNew(WorkerAction);
    }

    private Action<T> _workerMethod;

    private void WorkerAction()
    {
        lock (_processSyncObj)
        {
            if (_workerMethod == null)
                return;

            while (true)
            {
                T item;
                if (_queue.TryTake(out item))
                {
                    var method = _workerMethod;
                    if (method != null)
                        method(item);

                }
            }
        }
    }

    private BlockingCollection<T> _queue = new BlockingCollection<T>();
    private object _processSyncObj = new object();
    private volatile bool _isProcessing;

    public void EnqueueItem(T item)
    {
        // thought you might want to swap BlockingCollection with a normal collection since you apparently only want your read threadlocked? You're already making that sure in "WorkerAction"
        _queue.Add(item);
    }
}


/// <summary>
/// Usage example
/// </summary>
public class Program
{
    public void Start()
    {
        var test = new WorkerQueue<string>(WorkerMethod);
    }

    private void WorkerMethod(string s)
    {
        Console.WriteLine(s);
    }
}
公共类WorkerQueue
{
公共工作队列(操作工作方法)
{
_workerMethod=workerMethod;
Task.Factory.StartNew(WorkerAction);
}
私人行动法;
私有void WorkerAction()
{
锁(_processSyncObj)
{
如果(_workerMethod==null)
返回;
while(true)
{
T项;
if(_queue.TryTake(out项))
{
var方法=_workerMethod;
if(方法!=null)
方法(项目);
}
}
}
}
private BlockingCollection_queue=new BlockingCollection();
私有对象_processSyncObj=新对象();
私有易失性bool\u处理;
公共无效排队项(T项)
{
//您可能想将BlockingCollection与普通集合交换,因为您显然只希望读取线程锁定?您已经在“WorkerAction”中确定了这一点
_添加(项目);
}
}
/// 
///用法示例
/// 
公共课程
{
公开作废开始()
{
var测试=新WorkerQueue(WorkerMethod);
}
私有无效工作方法(字符串s)
{
控制台。写入线(s);
}
}

为什么不在“参与BlockingCollection”中启动任务?我想按顺序处理项目。这意味着,第一个项目应该完成处理,然后下一个项目应该被处理。如果我为每个项目启动任务,那么它们将被并行处理。或者,你的意思是别的吗?按队列顺序还是按每个队列排列?“数百个阻塞收集队列”是这个问题的起因。您没有说明为什么您认为这很有用。按每个队列的顺序排列。每个队列都是完全独立的。这看起来很有希望,我来看看。对不起,没有看到这个答案,因为我在写我的:)@Michael你的答案更广泛。干得好+1给你。