C# 使用线程和EventWaitHandle的生产者/消费者模式
我想这是一种代码审查,但这里是我对生产者/消费者模式的实现。我想知道的是,C# 使用线程和EventWaitHandle的生产者/消费者模式,c#,multithreading,producer-consumer,tpl-dataflow,blockingcollection,C#,Multithreading,Producer Consumer,Tpl Dataflow,Blockingcollection,我想这是一种代码审查,但这里是我对生产者/消费者模式的实现。我想知道的是,ReceivingThread()或SendingThread()方法中的while循环是否会停止执行。请注意,EnqueueSend(datasendenqueinfo)是从多个不同的线程调用的,我可能无法在这里使用任务,因为我必须在单独的线程中使用命令 private Thread mReceivingThread; private Thread mSendingThread; private Queue<Dat
ReceivingThread()
或SendingThread()
方法中的while循环是否会停止执行。请注意,EnqueueSend(datasendenqueinfo)
是从多个不同的线程调用的,我可能无法在这里使用任务,因为我必须在单独的线程中使用命令
private Thread mReceivingThread;
private Thread mSendingThread;
private Queue<DataRecievedEnqeueInfo> mReceivingThreadQueue;
private Queue<DataSendEnqeueInfo> mSendingThreadQueue;
private readonly object mReceivingQueueLock = new object();
private readonly object mSendingQueueLock = new object();
private bool mIsRunning;
EventWaitHandle mRcWaitHandle;
EventWaitHandle mSeWaitHandle;
private void ReceivingThread()
{
while (mIsRunning)
{
mRcWaitHandle.WaitOne();
DataRecievedEnqeueInfo item = null;
while (mReceivingThreadQueue.Count > 0)
{
lock (mReceivingQueueLock)
{
item = mReceivingThreadQueue.Dequeue();
}
ProcessReceivingItem(item);
}
mRcWaitHandle.Reset();
}
}
private void SendingThread()
{
while (mIsRunning)
{
mSeWaitHandle.WaitOne();
while (mSendingThreadQueue.Count > 0)
{
DataSendEnqeueInfo item = null;
lock (mSendingQueueLock)
{
item = mSendingThreadQueue.Dequeue();
}
ProcessSendingItem(item);
}
mSeWaitHandle.Reset();
}
}
internal void EnqueueRecevingData(DataRecievedEnqeueInfo info)
{
lock (mReceivingQueueLock)
{
mReceivingThreadQueue.Enqueue(info);
mRcWaitHandle.Set();
}
}
public void EnqueueSend(DataSendEnqeueInfo info)
{
lock (mSendingQueueLock)
{
mSendingThreadQueue.Enqueue(info);
mSeWaitHandle.Set();
}
}
私有线程mreceivingtread;
私有线程mSendingThread;
专用队列mreceivingtreadqueue;
专用队列MSendingReadQueue;
私有只读对象mReceivingQueueLock=新对象();
私有只读对象mSendingQueueLock=新对象();
私家车误行;
EventWaitHandle-mRcWaitHandle;
EventWaitHandle mSeWaitHandle;
私有void receivingtread()
{
同时(误运行)
{
mRcWaitHandle.WaitOne();
DataReceiveDenQueInfo项=null;
while(mreceivingtreadqueue.Count>0)
{
锁(mReceivingQueueLock)
{
item=mreceivingtreadqueue.Dequeue();
}
过程接收项目(项目);
}
mRcWaitHandle.Reset();
}
}
私有void SendingThread()
{
同时(误运行)
{
mSeWaitHandle.WaitOne();
而(MSEndingReadQueue.Count>0)
{
datasendenqueinfo项=null;
锁(mSendingQueueLock)
{
item=mSendingThreadQueue.Dequeue();
}
ProcessSendingItem(项目);
}
mSeWaitHandle.Reset();
}
}
内部void排队接收数据(DataReceiveDenQueInfo信息)
{
锁(mReceivingQueueLock)
{
mreceivingtreadqueue.Enqueue(信息);
mRcWaitHandle.Set();
}
}
public void排队发送(datasendenqueinfo)
{
锁(mSendingQueueLock)
{
msendingreadqueue.Enqueue(信息);
mSeWaitHandle.Set();
}
}
这里的想法是,当队列为空时,我使用WaitHandle
S使线程进入睡眠状态,并在新项目进入队列时发出启动信号
更新
我只想把这个留给那些可能试图使用TPL或tasks实现生产者/消费者模式的人。使用代替队列、EventWaitHandle和lock对象:
公共类数据信息{}
私有线程mreceivingtread;
私有线程mSendingThread;
私有阻塞收集队列;
private CancellationTokenSource receivingCts=新的CancellationTokenSource();
私有void receivingtread()
{
尝试
{
而(!receivingCts.iscancellationrequest)
{
//这将一直阻止,直到将项目添加到队列中或取消取消令牌
DataInfo item=queue.Take(receivingCts.Token);
过程接收项目(项目);
}
}
捕获(操作取消异常)
{
}
}
内部无效排队接收数据(数据信息)
{
//生成新项目时,只需将其添加到队列中即可
队列。添加(信息);
}
//要取消接收线程,请取消令牌
私有void cancelReceivingRead()
{
receivingCts.Cancel();
}
就个人而言,对于简单的生产者-消费者问题,我只会使用。无需手动编写您自己的同步逻辑。如果队列中没有项目,则使用线程也将阻塞
下面是使用此类时代码的外观:
private BlockingCollection<DataRecievedEnqeueInfo> mReceivingThreadQueue = new BlockingCollection<DataRecievedEnqeueInfo>();
private BlockingCollection<DataSendEnqeueInfo> mSendingThreadQueue = new BlockingCollection<DataSendEnqeueInfo>();
public void Stop()
{
// No need for mIsRunning. Makes the enumerables in the GetConsumingEnumerable() calls
// below to complete.
mReceivingThreadQueue.CompleteAdding();
mSendingThreadQueue.CompleteAdding();
}
private void ReceivingThread()
{
foreach (DataRecievedEnqeueInfo item in mReceivingThreadQueue.GetConsumingEnumerable())
{
ProcessReceivingItem(item);
}
}
private void SendingThread()
{
foreach (DataSendEnqeueInfo item in mSendingThreadQueue.GetConsumingEnumerable())
{
ProcessSendingItem(item);
}
}
internal void EnqueueRecevingData(DataRecievedEnqeueInfo info)
{
// You can also use TryAdd() if there is a possibility that you
// can add items after you have stopped. Otherwise, this can throw an
// an exception after CompleteAdding() has been called.
mReceivingThreadQueue.Add(info);
}
public void EnqueueSend(DataSendEnqeueInfo info)
{
mSendingThreadQueue.Add(info);
}
private BlockingCollection mreceivingtreadqueue=new BlockingCollection();
private BlockingCollection msendingreadqueue=new BlockingCollection();
公共停车场()
{
//无需错误运行。使GetConsumingEnumerable()调用中的可枚举项
//请在下面完成。
mreceivingtreadqueue.completedadding();
msEndingReadQueue.CompleteAdding();
}
私有void receivingtread()
{
foreach(MRReceivingReadQueue.GetConsumingEnumerable()中的DataReceiveDenQueInfo项)
{
过程接收项目(项目);
}
}
私有void SendingThread()
{
foreach(msEndingReadQueue.GetConsumingEnumerable()中的DataSendenQueInfo项)
{
ProcessSendingItem(项目);
}
}
内部void排队接收数据(DataReceiveDenQueInfo信息)
{
//如果可能需要,也可以使用TryAdd()
//可以在停止后添加项目。否则,这会引发
//已调用CompleteAdding()后的异常。
mreceivingtreadqueue.Add(信息);
}
public void排队发送(datasendenqueinfo)
{
msEndingReadQueue.Add(信息);
}
正如注释中所建议的,您也可以尝试使用这些块
据我所见,您有两个类似的管道,用于接收和发送,因此我假设您的类层次结构如下:
class EnqueueInfo { }
class DataRecievedEnqeueInfo : EnqueueInfo { }
class DataSendEnqeueInfo : EnqueueInfo { }
abstract class EnqueueInfoProcessor<T>
where T : EnqueueInfo
{
// here we will store all the messages received before the handling
private readonly BufferBlock<T> _buffer;
// simple action block for actual handling the items
private ActionBlock<T> _action;
// cancellation token to cancel the pipeline
public EnqueueInfoProcessor(CancellationToken token)
{
_buffer = new BufferBlock<T>(new DataflowBlockOptions { CancellationToken = token });
_action = new ActionBlock<T>(item => ProcessItem(item), new ExecutionDataflowBlockOptions
{
MaxDegreeOfParallelism = Environment.ProcessorCount,
CancellationToken = token
});
// we are linking two blocks so all the items from buffer
// will flow down to action block in order they've been received
_buffer.LinkTo(_action, new DataflowLinkOptions { PropagateCompletion = true });
}
public void PostItem(T item)
{
// synchronously wait for posting to complete
_buffer.Post(item);
}
public async Task SendItemAsync(T item)
{
// asynchronously wait for message to be posted
await _buffer.SendAsync(item);
}
// abstract method to implement
protected abstract void ProcessItem(T item);
}
我们可以组装一个抽象类,它将封装用于创建管道的逻辑,并提供用于处理项目的接口,如下所示:
class EnqueueInfo { }
class DataRecievedEnqeueInfo : EnqueueInfo { }
class DataSendEnqeueInfo : EnqueueInfo { }
abstract class EnqueueInfoProcessor<T>
where T : EnqueueInfo
{
// here we will store all the messages received before the handling
private readonly BufferBlock<T> _buffer;
// simple action block for actual handling the items
private ActionBlock<T> _action;
// cancellation token to cancel the pipeline
public EnqueueInfoProcessor(CancellationToken token)
{
_buffer = new BufferBlock<T>(new DataflowBlockOptions { CancellationToken = token });
_action = new ActionBlock<T>(item => ProcessItem(item), new ExecutionDataflowBlockOptions
{
MaxDegreeOfParallelism = Environment.ProcessorCount,
CancellationToken = token
});
// we are linking two blocks so all the items from buffer
// will flow down to action block in order they've been received
_buffer.LinkTo(_action, new DataflowLinkOptions { PropagateCompletion = true });
}
public void PostItem(T item)
{
// synchronously wait for posting to complete
_buffer.Post(item);
}
public async Task SendItemAsync(T item)
{
// asynchronously wait for message to be posted
await _buffer.SendAsync(item);
}
// abstract method to implement
protected abstract void ProcessItem(T item);
}
如果您的消息流是关于
ReceiveInfo
消息变成SendInfo
的,则您还可以使用创建更复杂的管道。如果您的消息流是关于ReceiveInfo
消息,则最好使用为您处理所有同步逻辑的。阻止收集是一个不错的选择,但是,您也可以尝试。如果需要,我可以提供一些示例逻辑。@VMAtm我一直想了解TPL数据流。如果您能为我提供一些示例逻辑,当然,前提是不太麻烦。@SushantPoojary我已经用一些示例添加了答案。请看一看。如果我没记错的话,请阻止collection.getConsuminagenumerable()不一定会按照集合排队的顺序返回集合,这对我来说是个问题。我还希望线程在集合为空时休眠,即