Warning: file_get_contents(/data/phpspider/zhask/data//catemap/2/.net/24.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_Events_Queue - Fatal编程技术网

C# 接收新元素时,在线程中不断循环列表

C# 接收新元素时,在线程中不断循环列表,c#,.net,events,queue,C#,.net,Events,Queue,我真的不知道如何处理这个问题,但我订阅的是自定义类中触发的事件,理想情况下,我希望对它们进行排队,并在它们进入时先入先出处理它们。我知道队列,我想我应该用这个?但我的问题是在事件处理程序中,当收到我的消息时,我是否会简单地Enqueue()到那里的队列中,如果是这样,那么在添加新项目时如何处理队列 我正在考虑在构造函数中调用一个方法,该方法执行以下操作(请不要挂断帽子): 肯定有更优雅的方式来做到这一点?这将有效地命中myQueue并进行迭代,因为它包含元素并执行我想要的操作。表演会是什么样子?

我真的不知道如何处理这个问题,但我订阅的是自定义类中触发的事件,理想情况下,我希望对它们进行排队,并在它们进入时先入先出处理它们。我知道队列,我想我应该用这个?但我的问题是在事件处理程序中,当收到我的消息时,我是否会简单地
Enqueue()
到那里的队列中,如果是这样,那么在添加新项目时如何处理队列

我正在考虑在构造函数中调用一个方法,该方法执行以下操作(请不要挂断帽子):


肯定有更优雅的方式来做到这一点?这将有效地命中myQueue并进行迭代,因为它包含元素并执行我想要的操作。表演会是什么样子?我可以在一个单独的线程上生成它,以避免任何线程阻塞,我只是有时间接受
,而(true)

这是一个典型的生产者/消费者问题。快速的网络搜索揭示了这一点


您不希望执行while(true)循环,因为您的线程将消耗100%的CPU,即使它没有工作,也可能会耗尽系统中的其他线程。

您在那里执行的操作看起来不正确。如果您确实在使用队列,那么您应该真正将项目从队列中拉出,而不是在其上迭代:

while (!queue.empty()) // or whatever
{
  process the first item in the queue
}

如果您是从多个线程运行此操作,则需要引入某种形式的锁定,以防止队列同步出现问题。您可能还应该确保在对元素进行排队以及对元素进行出列时,队列被锁定

如果处理队列的线程除了此之外什么也不做,那么您的基本代码可能就可以了,但处理队列当前为空的情况除外。您可能应该添加以下内容:

while (myQueue.Count == 0)
    Thread.Sleep(sleepTime);
while (myQueue.Count == 0)
    Thread.Sleep(sleepTime);
while (myQueue.Count > 0)
{
    lock(myLock)
        myObject = myQueue.Dequeue();
    // do your work...
}
这将为您提供一种“等待”的方式,直到您的事件填满您的队列

此外,当您从队列中退出队列时,您将无法使用foreach。您将需要执行以下操作:

while (myQueue.Count == 0)
    Thread.Sleep(sleepTime);
while (myQueue.Count == 0)
    Thread.Sleep(sleepTime);
while (myQueue.Count > 0)
{
    lock(myLock)
        myObject = myQueue.Dequeue();
    // do your work...
}
如果有人添加到您的队列中,这将防止集合修改问题,并使您不必在处理元素的整个过程中锁定


编辑:我同意一些评论,这并不总是最有效的策略

如果队列很大程度上是空的,并且偶尔只有元素,我会创建一个自定义类来包装它

当队列变为空/非空时,可能会触发一个事件。一旦队列接收到项目,事件就会触发一个线程来处理它并清空它。一旦达到0项,它可能会有一个事件来停止处理


如果队列的状态大部分时间为空,这将比持续等待有效得多。另一方面,如果队列几乎总是满的,而处理线程很少跟上,为了简单起见,我会使用上面的方法。

你不能这样做。如果基础集合在枚举数仍在使用时发生更改,则为foreach返回的枚举数将引发异常


基本上,您需要做的是设置另一个线程来处理事件。理想情况下,您应该(通过事件或其他同步机制)向另一个线程发出信号,表示有可用的工作。它将使工作项出列,处理它,然后休眠,直到下一个信号出现。或者,您可以使用轮询(定期唤醒并检查另一个项目),但效率较低。在任何情况下,都需要使用锁定来确保不会同时在两个线程中修改队列。

我通常使用ManualResetEvent来表示元素已添加到集合中

事件处理程序执行如下操作:

lock (myLock)
{
   myq.Enqueue(...);
   myqAddedSignal.Set();
}
处理线程等待信号-一旦发出信号,它清空队列,重置信号,然后处理项目:

while (true)
{
   myqAddedSignal.WaitOne();
   lock (myLock)
   {
      // pull everything off myQ into a list
      // clear myQ
      myqAddedSignal.Reset();
   }

   foreach (obj in copyOfMyQ)
   {
      ...
   }
}

这将以线程安全的方式处理队列中的项目。唯一的共享状态是myqAddedSignal—对它的访问在myLock上是同步的(我通常只是将其作为一个对象)。

您可以看到,它有这样一个队列。

根据Reed给出的建议,我创建了一个自定义类,并在队列变为空且已填充时引发事件

自定义
EventQueue
类:

public class EventQueue<T> : Queue<T>
{
    public delegate void OnQueueMadeEmptyDelegate();
    public event OnQueueMadeEmptyDelegate OnQueueMadeEmpty;
    public delegate void OnQueueMadeNonEmptyDelegate();
    public event OnQueueMadeNonEmptyDelegate OnQueueMadeNonEmpty;

    public new void Enqueue(T item)
    {
        var oldCount = Count;
        base.Enqueue(item);
        if (OnQueueMadeNonEmpty != null &&
            oldCount == 0 && Count > 0)
            // FIRE EVENT
            OnQueueMadeNonEmpty();
    }
    public new T Dequeue()
    {
        var oldCount = Count;
        var item = base.Dequeue();
        if (OnQueueMadeEmpty != null &&
            oldCount > 0 && Count == 0)
        {
            // FIRE EVENT
            OnQueueMadeEmpty();
        }
        return item;
    }
    public new void Clear()
    {
        base.Clear();
        if (OnQueueMadeEmpty != null)
        {
            // FIRE EVENT
            OnQueueMadeEmpty();
        }
    }
}
在主体类中:

private void InitQueue()
{
    this.translationQueue = new EventQueue<QueueRequest>();
    this.translationQueue.OnQueueMadeEmpty += new EventQueue<QueueRequest>.OnQueueMadeEmptyDelegate(translationQueue_OnQueueMadeEmpty);
    this.translationQueue.OnQueueMadeNonEmpty += new EventQueue<QueueRequest>.OnQueueMadeNonEmptyDelegate(translationQueue_OnQueueMadeNonEmpty);
}

void translationQueue_OnQueueMadeNonEmpty()
{
    while (translationQueue.Count() > 0)
    {
        lock (queueLock)
        {
            QueueRequest request = translationQueue.Dequeue();
#if DEBUG
            System.Diagnostics.Debug.WriteLine("Item taken from queue...");
#endif
            // hard work
            ....
            ....
            ....
        }
    }
}

void translationQueue_OnQueueMadeEmpty()
{
    // empty queue
    // don't actually need to do anything here?
}

private void onMessageReceived(....)
{
  ....
  ....
  ....
  // QUEUE REQUEST
  lock (queueLock)
  {
    QueueRequest queueRequest = new QueueRequest
                                    {
                                        Request = request,
                                        Sender = sender,
                                        Recipient = tcpClientService
                                    };
    translationQueue.Enqueue(queueRequest);
#if DEBUG
    System.Diagnostics.Debug.WriteLine("Item added to queue...");
#endif
  }
}
我知道有很多,但希望你们检查一下完整的实现。你怎么认为?我执行锁定的方式是否正确


如果这还可以的话,我会奖励Reed,因为我的解决方案是根据他的想法创建的。

您正在寻找的结构是一个信号量

向队列中添加一个项目,然后向信号量中添加一个计数

您在另一个线程中等待信号量,并处理队列中的一个项目


根据队列实现(如BCL实现),您必须在检索时锁定/解锁。

不错的建议。谢谢你,里德。我会考虑一下,是的,我的线除了这个什么也做不了,我很高兴。请不要这样做。繁忙的等待会影响性能和电池寿命。如果您需要等待队列中有一些项目,那么应该使用某种类型的事件,这就是我想要做的,这是我在队列类上检查的第一件事,一个事件处理程序,但没有。我想我可以通过从队列继承并重载Enqueue命令来创建自己的队列?你怎么看?@1800信息:我有时会同意,这取决于事物添加到队列中的频率。如果队列通常包含项目,则很少有效地等待(并处理等待情况)。如果队列大部分是空的,很少有项目,我会尝试以不同的方式实现它。@GoNeale:很高兴我们能提供帮助。@1800,我在Reed的帖子中有一些评论。@1800,没有
private void InitQueue()
{
    this.translationQueue = new EventQueue<QueueRequest>();
    this.translationQueue.OnQueueMadeEmpty += new EventQueue<QueueRequest>.OnQueueMadeEmptyDelegate(translationQueue_OnQueueMadeEmpty);
    this.translationQueue.OnQueueMadeNonEmpty += new EventQueue<QueueRequest>.OnQueueMadeNonEmptyDelegate(translationQueue_OnQueueMadeNonEmpty);
}

void translationQueue_OnQueueMadeNonEmpty()
{
    while (translationQueue.Count() > 0)
    {
        lock (queueLock)
        {
            QueueRequest request = translationQueue.Dequeue();
#if DEBUG
            System.Diagnostics.Debug.WriteLine("Item taken from queue...");
#endif
            // hard work
            ....
            ....
            ....
        }
    }
}

void translationQueue_OnQueueMadeEmpty()
{
    // empty queue
    // don't actually need to do anything here?
}

private void onMessageReceived(....)
{
  ....
  ....
  ....
  // QUEUE REQUEST
  lock (queueLock)
  {
    QueueRequest queueRequest = new QueueRequest
                                    {
                                        Request = request,
                                        Sender = sender,
                                        Recipient = tcpClientService
                                    };
    translationQueue.Enqueue(queueRequest);
#if DEBUG
    System.Diagnostics.Debug.WriteLine("Item added to queue...");
#endif
  }
}
public struct QueueRequest
{
    public MessageTranslateRequest Request { get; set; }
    public TCPClientService Sender { get; set; }
    public TCPClientService Recipient { get; set; }
}