C# 在队列中添加数据时通知线程
我有一个线程正在队列中添加数据,现在我希望在添加数据时通知另一个线程,以便它可以开始处理队列中的数据 一个选择是线程将连续轮询队列,查看计数是否大于零,但我认为这不是一个好方法,任何其他建议都将不胜感激 任何关于如何实现这一点的建议,我都使用.NETFramework3.5C# 在队列中添加数据时通知线程,c#,multithreading,thread-safety,queue,C#,Multithreading,Thread Safety,Queue,我有一个线程正在队列中添加数据,现在我希望在添加数据时通知另一个线程,以便它可以开始处理队列中的数据 一个选择是线程将连续轮询队列,查看计数是否大于零,但我认为这不是一个好方法,任何其他建议都将不胜感激 任何关于如何实现这一点的建议,我都使用.NETFramework3.5 如果我有两个线程,一个在执行q.Enqueue(data),另一个在执行q.dequeue(),在这种情况下,我需要管理锁吗?我认为阻塞收集比排队更好。除此之外,持续检查队列大小(并在其为零时暂停线程)是非常好的方法 顺便说
如果我有两个线程,一个在执行
q.Enqueue(data)
,另一个在执行q.dequeue()
,在这种情况下,我需要管理锁吗?我认为阻塞收集比排队更好。除此之外,持续检查队列大小(并在其为零时暂停线程)是非常好的方法
顺便说一句,我们这里讨论的是生产者-消费者模式。我想你可以在谷歌上搜索其他方法。你可以使用
ManualResetEvent
通知线程
ManualResetEvent e = new ManualResetEvent(false);
每次q.enqueue()之后
doe.Set()
并在处理线程中,使用e.WaitOne()
等待项目
如果在循环内进行处理,则应该在
e.WaitOne()
之后立即执行e.Reset()
,我不使用队列,因为我更愿意批处理它们。这在您必须打开/关闭(日志)文件、打开/关闭数据库时更有用。以下是我如何创建这样一个示例:
// J. van Langen
public abstract class QueueHandler<T> : IDisposable
{
// some events to trigger.
ManualResetEvent _terminating = new ManualResetEvent(false);
ManualResetEvent _terminated = new ManualResetEvent(false);
AutoResetEvent _needProcessing = new AutoResetEvent(false);
// my 'queue'
private List<T> _queue = new List<T>();
public QueueHandler()
{
new Thread(new ThreadStart(() =>
{
// what handles it should wait on.
WaitHandle[] handles = new WaitHandle[] { _terminating, _needProcessing };
// while not terminating, loop (0 timeout)
while (!_terminating.WaitOne(0))
{
// wait on the _terminating and the _needprocessing handle.
WaitHandle.WaitAny(handles);
// my temporay array to store the current items.
T[] itemsCopy;
// lock the queue
lock (_queue)
{
// create a 'copy'
itemsCopy = _queue.ToArray();
// clear the queue.
_queue.Clear();
}
if (itemsCopy.Length > 0)
HandleItems(itemsCopy);
}
// the thread is done.
_terminated.Set();
})).Start();
}
public abstract void HandleItems(T[] items);
public void Enqueue(T item)
{
// lock the queue to add the item.
lock (_queue)
_queue.Add(item);
_needProcessing.Set();
}
// batch
public void Enqueue(IEnumerable<T> items)
{
// lock the queue to add multiple items.
lock (_queue)
_queue.AddRange(items);
_needProcessing.Set();
}
public void Dispose()
{
// let the thread know it should stop.
_terminating.Set();
// wait until the thread is stopped.
_terminated.WaitOne();
}
}
使用
BlockingCollection
类。它的好处是,如果队列为空,Take
方法会阻塞(不进行轮询)。它包含在.NET4.0+中,或者作为下载的一部分,或者甚至可能是。如果需要,可以使用类的以下未优化变体
public class BlockingCollection<T>
{
private readonly Queue<T> m_Queue = new Queue<T>();
public void Add(T item)
{
lock (m_Queue)
{
m_Queue.Enqueue(item);
Monitor.Pulse(m_Queue);
}
}
public T Take()
{
lock (m_Queue)
{
while (m_Queue.Count == 0)
{
Monitor.Wait(m_Queue);
}
return m_Queue.Dequeue();
}
}
public bool TryTake(out T item)
{
item = default(T);
lock (m_Queue)
{
if (m_Queue.Count > 0)
{
item = m_Queue.Dequeue();
}
}
return item != null;
}
}
公共类阻止集合
{
私有只读队列m_Queue=新队列();
公共作废新增(T项)
{
锁(m_队列)
{
m_队列。排队(项目);
监视器脉冲(m_队列);
}
}
公众不接受
{
锁(m_队列)
{
while(m_Queue.Count==0)
{
监视器。等待(m_队列);
}
返回m_Queue.Dequeue();
}
}
公共图书馆试镜(外T项目)
{
项目=默认值(T);
锁(m_队列)
{
如果(m_Queue.Count>0)
{
item=m_Queue.Dequeue();
}
}
退货项目!=空;
}
}
或使用ManualResetEvent
或AutoResetEvent
是的,您需要为队列设置锁。但是第二个线程不知道什么时候有东西被添加到队列中。如果不想轮询(queue.peek),可以使用事件触发第二个线程,但仍然需要锁定队列。看看我的例子,这是处理事件(无轮询),并锁定队列。哎哟,我非常确定BC是旧的8(感谢目前的详细解释ManualResetEvent从您的代码中对我来说是可以的。我建议改用ManualResetEvent来使用AutoResetEvent。ManualResetEvent.Reset()在WaitOne()和Reset()之间排队时可能会出错
public class BlockingCollection<T>
{
private readonly Queue<T> m_Queue = new Queue<T>();
public void Add(T item)
{
lock (m_Queue)
{
m_Queue.Enqueue(item);
Monitor.Pulse(m_Queue);
}
}
public T Take()
{
lock (m_Queue)
{
while (m_Queue.Count == 0)
{
Monitor.Wait(m_Queue);
}
return m_Queue.Dequeue();
}
}
public bool TryTake(out T item)
{
item = default(T);
lock (m_Queue)
{
if (m_Queue.Count > 0)
{
item = m_Queue.Dequeue();
}
}
return item != null;
}
}