Warning: file_get_contents(/data/phpspider/zhask/data//catemap/2/csharp/276.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

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# 使用列表的消费者/生产者模式_C#_.net_Concurrency_Queue - Fatal编程技术网

C# 使用列表的消费者/生产者模式

C# 使用列表的消费者/生产者模式,c#,.net,concurrency,queue,C#,.net,Concurrency,Queue,我有一个WinForms应用程序,有一个消费者和一个生产者任务。我的producer任务定期连接到web服务并检索指定数量的字符串,然后需要将这些字符串放入某种并发的固定大小FIFO队列中。然后,我的使用者任务处理这些字符串,然后作为SMS消息发送出去(每条消息一个字符串)。我的生产者任务调用的SOAP函数需要一个参数来指定我想要获取的字符串数。此数字将由队列中的可用空间决定。因此,如果我的最大队列大小为100个字符串,并且在我的制作人下次轮询web服务时队列中有60个字符串,那么我需要它请求4

我有一个WinForms应用程序,有一个消费者和一个生产者任务。我的producer任务定期连接到web服务并检索指定数量的字符串,然后需要将这些字符串放入某种并发的固定大小FIFO队列中。然后,我的使用者任务处理这些字符串,然后作为SMS消息发送出去(每条消息一个字符串)。我的生产者任务调用的SOAP函数需要一个参数来指定我想要获取的字符串数。此数字将由队列中的可用空间决定。因此,如果我的最大队列大小为100个字符串,并且在我的制作人下次轮询web服务时队列中有60个字符串,那么我需要它请求40个字符串,因为此时我的队列中只能容纳这些字符串

下面是我用来表示固定大小FIFO队列的代码:

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, queue.Count - Size);
            }
        }
    }

    public T[] Dequeue()
    {
        lock (syncObj)
        {
            var result = queue.ToArray();
            queue.Clear();
            return result;
        }
    }

    public T Peek()
    {
        lock (syncObj)
        {
            var result = queue[0];
            return result;
        }
    }

    public int GetCount()
    {
        lock (syncObj)
        {
            return queue.Count;
        }
    }
public类FixedSizeQueue
{
私有只读列表队列=新列表();
私有只读对象syncObj=新对象();
公共整数大小{get;私有集;}
公共固定大小(整数大小)
{
大小=大小;
}
公共无效排队(T obj)
{
锁(同步对象)
{
queue.Insert(0,obj);
如果(queue.Count>大小)
{
queue.RemoveRange(大小,queue.Count-Size);
}
}
}
公共T[]退出队列()
{
锁(同步对象)
{
var result=queue.ToArray();
queue.Clear();
返回结果;
}
}
公共T Peek()
{
锁(同步对象)
{
var结果=队列[0];
返回结果;
}
}
public int GetCount()
{
锁(同步对象)
{
返回队列。计数;
}
}
我的producer任务目前没有指定我从web服务中需要的字符串数,但它似乎很简单,只需获取队列中的当前项目数(q.GetCount()),然后从最大队列大小中减去它。然而,即使GetCount()使用锁,是否有可能在GetCount()之后退出时,我的使用者任务可能会处理队列中的10个字符串,这意味着我永远无法保持队列100%满


此外,我的消费者任务基本上需要在尝试在SMS消息中发送消息之前“偷看”队列中的第一个字符串。如果消息无法发送,我需要将字符串保留在队列中的原始位置。我完成这项任务的第一个想法是“偷看”在队列中的第一个字符串处,尝试在SMS消息中发送该字符串,如果发送成功,则将其从队列中删除。这样,如果发送失败,该字符串仍在队列中的原始位置。这听起来合理吗?

这是一个广泛的问题,因此确实没有明确的答案,但以下是我的想法。

然而,即使GetCount()使用锁,难道不可能在GetCount()退出时,我的使用者任务就可以处理队列中的10个字符串,这意味着我永远无法保持队列100%的满状态吗

是的,除非您在整个web服务查询期间锁定
syncObj
。但生产者/消费者的目的是允许消费者在生产者获取更多信息的同时处理项目。对此,您确实无能为力;在某个时刻,队列将不会100%满。如果总是100%满那就意味着消费者什么也没做

这样,如果发送失败,字符串仍在队列中的原始位置。这听起来合理吗

也许是这样,但按照您的编码方式,
Dequeue()
操作将返回队列的整个状态并将其清除。给定此接口,您唯一的选择是将失败的项目重新排队,以便稍后处理,这是一种完全合理的技术

<>我也会考虑为消费者添加一个方法来阻止自己,直到有要处理的项目。例如:

public T[] WaitForItemAndDequeue(TimeSpan timeout)
{
    lock (syncObj) {
        if (queue.Count == 0 && !Monitor.Wait(syncObj, timeout)) {
            return null; // Timeout expired
        }

        return Dequeue();
    }
}

public T[] WaitForItem()
{
    lock (syncObj) {
        while (queue.Count != 0) {
            Monitor.Wait(syncObj);
        }

        return Dequeue();
    }
}

然后,您必须更改
Enqueue()
以在操作列表后调用
Monitor.Pulse(syncObj)
(因此在方法末尾,但在
块内部)

你可能想考虑使用<代码> Stasy.Cuff.Orth.CurrutsReals而不是<代码>列表<代码>,因为这将为你处理大量的锁定。谢谢CDHOWIE。这些都是很好的建议,我感谢你抽出时间来评论。回想起来,我意识到这是一个非常开放的问题。我应该有。在发帖前缩小焦点做得更好。