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; }
}