Warning: file_get_contents(/data/phpspider/zhask/data//catemap/2/.net/21.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
C# 具有固定大小FIFO队列的生产者/消费者模式_C#_.net_Queue_Producer Consumer_Concurrent Collections - Fatal编程技术网

C# 具有固定大小FIFO队列的生产者/消费者模式

C# 具有固定大小FIFO队列的生产者/消费者模式,c#,.net,queue,producer-consumer,concurrent-collections,C#,.net,Queue,Producer Consumer,Concurrent Collections,我需要围绕固定大小的FIFO队列实现生产者/消费者模式。我认为围绕ConcurrentQueue的包装器类可能适用于此,但我不能完全确定(而且我以前从未使用过ConcurrentQueue)。这里的转折点是队列只需要保存固定数量的项目(在我的例子中是字符串)。我的应用程序将有一个生产者任务/线程和一个消费者任务/线程。当我的使用者任务运行时,它需要及时将队列中存在的所有项目出列并处理它们 值得一提的是,我的消费者对排队项目的处理无非是通过SOAP将它们上传到一个并非100%可靠的web应用程序。

我需要围绕固定大小的FIFO队列实现生产者/消费者模式。我认为围绕ConcurrentQueue的包装器类可能适用于此,但我不能完全确定(而且我以前从未使用过ConcurrentQueue)。这里的转折点是队列只需要保存固定数量的项目(在我的例子中是字符串)。我的应用程序将有一个生产者任务/线程和一个消费者任务/线程。当我的使用者任务运行时,它需要及时将队列中存在的所有项目出列并处理它们

值得一提的是,我的消费者对排队项目的处理无非是通过SOAP将它们上传到一个并非100%可靠的web应用程序。如果无法建立连接或调用SOAP调用失败,我应该放弃这些项并返回队列以获取更多信息。由于SOAP的开销,我试图在一次SOAP调用中最大化队列中可以发送的项目数

有时,我的生产者添加项目的速度可能快于我的消费者删除和处理项目的速度。如果队列已满,并且我的制作人需要添加另一个项目,则我需要将新项目排队,但将最旧的项目退出队列,以便队列大小保持不变。基本上,我需要始终保留队列中生成的最新项目(即使这意味着某些项目不会被消费,因为我的消费者当前正在处理以前的项目)

关于制作人保持队列中项目数量不变的问题,我从这个问题中发现了一个潜在的想法:

我目前正在ConcurrentQueue周围使用一个包装器类(基于该答案),其Enqueue()方法如下:

public class FixedSizeQueue<T>
{
    readonly ConcurrentQueue<T> queue = new ConcurrentQueue<T>();

    public int Size { get; private set; }

    public FixedSizeQueue(int size)
    {
        Size = size;
    }

    public void Enqueue(T obj)
    {
        // add item to the queue
        queue.Enqueue(obj);

        lock (this) // lock queue so that queue.Count is reliable
        {
            while (queue.Count > Size) // if queue count > max queue size, then dequeue an item
            {
                T objOut;
                queue.TryDequeue(out objOut);
            }
        }
    }
}
FixedSizeQueue<string> incomingMessageQueue = new FixedSizeQueue<string>(10); // 10 item limit
public类FixedSizeQueue
{
只读ConcurrentQueue=新ConcurrentQueue();
公共整数大小{get;私有集;}
公共固定大小(整数大小)
{
大小=大小;
}
公共无效排队(T obj)
{
//将项目添加到队列中
排队;排队(obj);
lock(this)//锁定队列,使queue.Count可靠
{
while(queue.Count>Size)//如果queue Count>max queue Size,则将项目出列
{
T objOut;
queue.TryDequeue(out对象);
}
}
}
}
我创建了一个此类的实例,该类的大小限制如下:

public class FixedSizeQueue<T>
{
    readonly ConcurrentQueue<T> queue = new ConcurrentQueue<T>();

    public int Size { get; private set; }

    public FixedSizeQueue(int size)
    {
        Size = size;
    }

    public void Enqueue(T obj)
    {
        // add item to the queue
        queue.Enqueue(obj);

        lock (this) // lock queue so that queue.Count is reliable
        {
            while (queue.Count > Size) // if queue count > max queue size, then dequeue an item
            {
                T objOut;
                queue.TryDequeue(out objOut);
            }
        }
    }
}
FixedSizeQueue<string> incomingMessageQueue = new FixedSizeQueue<string>(10); // 10 item limit
FixedSizeQueue incomingMessageQueue=newfixedsizequeue(10);//10项限额

我启动producer任务,它开始填充队列。my Enqueue()方法中的代码在添加项目导致队列计数超过最大大小时,似乎可以从队列中删除最旧的项目。现在我需要我的消费者任务把物品排出来并处理它们,但这是我的大脑感到困惑的地方。为我的消费者实施出列方法的最佳方法是什么?该方法将在某一时刻拍摄队列快照,并将所有项目出列以进行处理(生产商在此过程中可能仍在向队列中添加项目)?

您不想对
使用
锁定
。有关更多详细信息,请参阅

此代码

// if queue count > max queue size, then dequeue an item
while (queue.Count > Size) 
{
    T objOut;
    queue.TryDequeue(out objOut);
}

建议您需要以某种方式等待或通知消费者商品的可用性。在这种情况下,考虑使用。

< P>简单地说,并发队列有一个“toRoad”方法,当输入时,它将锁定集合并生成队列中所有当前项的“快照”。如果您希望为消费者提供一块要处理的对象,可以锁定排队方法拥有的同一对象,调用ToArray(),然后在返回提取的数组之前,通过
while(!queue.IsEmpty)queue.TryDequeue(out trash)
循环清除队列

这将是您的
GetAll()
方法:

public T[] GetAll()
{
    lock (syncObj) // so that we don't clear items we didn't get with ToArray()
    {
        var result = queue.ToArray();
        T trash;
        while(!queue.IsEmpty) queue.TryDequeue(out trash);
    }
}
由于您必须清除队列,因此可以简单地将这两个操作结合起来;创建一个大小合适的数组(使用queue.Count),然后在队列不为空时,将一个项目出列并将其放入数组中,然后返回

这就是具体问题的答案。我现在必须凭良心戴上我的CodeReview.SE帽子,并指出以下几点:

  • 切勿使用
    锁定(此)
    。您永远不知道其他对象可能会将您的对象用作锁定焦点,因此当对象从内部锁定自身时,会被锁定。最好的做法是锁定一个私有范围的对象实例,通常是为了锁定而创建的实例:
    private readonly object syncObj=new object()

  • 因为您正在锁定包装器的关键部分,所以我将使用普通的
    列表
    ,而不是并发集合。访问速度更快,更容易清除,因此您可以比ConcurrentQueue更简单地完成您正在做的事情。要排队,请锁定同步对象,在索引零之前插入(),然后使用RemoveRange()将索引大小中的所有项删除到列表的当前计数。要退出队列,请锁定同一个同步对象,调用myList.ToArray()(从Linq命名空间;执行与ConcurrentQueue几乎相同的操作),然后在返回数组之前调用myList.Clear()。再简单不过了:

    public class FixedSizeQueue<T>
    {
    private readonly List<T> queue = new List<T>();
    private readonly object syncObj = new object();
    
    public int Size { get; private set; }
    
    public FixedSizeQueue(int size) { Size = size; }
    
    public void Enqueue(T obj)
    {
        lock (syncObj)
        {
            queue.Insert(0,obj)
            if(queue.Count > Size) 
               queue.RemoveRange(Size, Count-Size);
        }
    }
    
    public T[] Dequeue()
    {
        lock (syncObj)
        {
            var result = queue.ToArray();
            queue.Clear();
            return result;
        }
    }
    }
    
    public类FixedSizeQueue
    {
    私有只读列表队列=新列表();
    私有只读对象syncObj=新对象();
    公共整数大小{get;私有集;}
    public FixedSizeQueue(int size){size=size;}
    公共无效排队(T obj)
    {
    锁(同步对象)
    {
    queue.Insert(0,obj)
    如果(queue.Count>大小)
    queue.RemoveRange(大小、计数大小);
    }
    }
    公共T[]退出队列()
    {
    锁(同步对象)
    {
    var result=queue.ToArray();
    queue.Clear();
    返回结果;
    }
    }
    }
    
  • 您似乎明白,您正在使用此模型丢弃排队的项目。那是