Warning: file_get_contents(/data/phpspider/zhask/data//catemap/2/csharp/330.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# 使用线程和EventWaitHandle的生产者/消费者模式_C#_Multithreading_Producer Consumer_Tpl Dataflow_Blockingcollection - Fatal编程技术网

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()不一定会按照集合排队的顺序返回集合,这对我来说是个问题。我还希望线程在集合为空时休眠,即