C#生产者/消费者 [英] C# producer/consumer

查看:154
本文介绍了C#生产者/消费者的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我最近遇到了一个生产者/消费者模式c#实现。这非常简单(至少对我来说)非常优雅。



似乎是在2006年左右设计的,所以我想知道这个实现是否是

- 安全

- 仍然适用



代码如下(原始代码在 http://bytes.com/topic/net/answers/575276-producer-consumer#post2251375

  using System; 
使用System.Collections;
使用System.Threading;

public class Test
{
static ProducerConsumer queue;

static void Main()
{
queue = new ProducerConsumer();
new Thread(new ThreadStart(ConsumerJob))。Start();

随机rng = new Random(0); (int i = 0; i< 10; i ++)
{
Console.WriteLine(Producing {0},i);
queue.Produce(i);
Thread.Sleep(rng.Next(1000));
}
}

static void ConsumerJob()
{
//确保我们从
//获取不同的随机种子第一个线程
随机rng = new Random(1);
//我们碰巧知道我们只有10
//项目来接收
(int i = 0; i< 10; i ++)
{
object o = queue.Consume();
Console.WriteLine(\t\t\t\tConsuming {0},o);
Thread.Sleep(rng.Next(1000));
}
}
}

public class ProducerConsumer
{
readonly object listLock = new object();
队列队列=新队列();

public void Produce(object o)
{
lock(listLock)
{
queue.Enqueue(o);

//我们总是需要脉冲,即使队列不是
//空之前。否则,如果我们快速连续地添加了几个项目
//,我们可能只会一次冲动,即使有多个线程
//等待项目,也可以单个线程唤醒
// 。
Monitor.Pulse(listLock);
}
}

public object Consume()
{
lock(listLock)
{
//如果队列是空的,等待一个项目被添加
//注意这是一个while循环,因为我们可能被脉冲
//但是在另一个线程进来之前没有唤醒
//消耗新添加的对象。在这种情况下,我们要
//必须等待另一个脉冲。
while(queue.Count == 0)
{
//发布listLock,只重新获取
//在通过调用Pulse
唤醒Monitor.Wait(listLock);
}
return queue.Dequeue();
}
}
}


解决方案

代码比那个旧 - 我在.NET 2.0出来之前写了一段时间。生产者/消费者队列的概念 更旧,但是:)



尽管我知道这是安全的 - 但它有一些缺陷:




  • 它是非通用的。现代版本一定是通用的。

  • 没有办法停止队列。停止队列(使所有消费者线程退出)的一种简单方法是具有可以放入队列的停止工作令牌。然后添加与线程一样多的令牌。或者,您有一个单独的标志来表示您要停止。 (这样可以在完成队列中的所有当前工作之前停止其他线程。)

  • 如果作业非常小,一次使用单个作业可能不是最有效的要做的事情。



老实说,代码背后的想法比代码本身更重要。 $ b

i've recently come across a producer/consumer pattern c# implementation. it's very simple and (for me at least) very elegant.

it seems to have been devised around 2006, so i was wondering if this implementation is
- safe
- still applicable

Code is below (original code was referenced at http://bytes.com/topic/net/answers/575276-producer-consumer#post2251375)

using System;  
using System.Collections;  
using System.Threading;

public class Test
{  
    static ProducerConsumer queue;

    static void Main()
    {
        queue = new ProducerConsumer();
        new Thread(new ThreadStart(ConsumerJob)).Start();

        Random rng = new Random(0);
        for (int i=0; i < 10; i++)
        {
            Console.WriteLine ("Producing {0}", i);
            queue.Produce(i);
            Thread.Sleep(rng.Next(1000));
        }
    }

    static void ConsumerJob()
    {
        // Make sure we get a different random seed from the
        // first thread
        Random rng = new Random(1);
        // We happen to know we've only got 10 
        // items to receive
        for (int i=0; i < 10; i++)
        {
            object o = queue.Consume();
            Console.WriteLine ("\t\t\t\tConsuming {0}", o);
            Thread.Sleep(rng.Next(1000));
        }
    }
}

public class ProducerConsumer
{
    readonly object listLock = new object();
    Queue queue = new Queue();

    public void Produce(object o)
    {
        lock (listLock)
        {
            queue.Enqueue(o);

            // We always need to pulse, even if the queue wasn't
            // empty before. Otherwise, if we add several items
            // in quick succession, we may only pulse once, waking
            // a single thread up, even if there are multiple threads
            // waiting for items.            
            Monitor.Pulse(listLock);
        }
    }

    public object Consume()
    {
        lock (listLock)
        {
            // If the queue is empty, wait for an item to be added
            // Note that this is a while loop, as we may be pulsed
            // but not wake up before another thread has come in and
            // consumed the newly added object. In that case, we'll
            // have to wait for another pulse.
            while (queue.Count==0)
            {
                // This releases listLock, only reacquiring it
                // after being woken up by a call to Pulse
                Monitor.Wait(listLock);
            }
            return queue.Dequeue();
        }
    }
}

解决方案

The code is older than that - I wrote it some time before .NET 2.0 came out. The concept of a producer/consumer queue is way older than that though :)

Yes, that code is safe as far as I'm aware - but it has some deficiencies:

  • It's non-generic. A modern version would certainly be generic.
  • It has no way of stopping the queue. One simple way of stopping the queue (so that all the consumer threads retire) is to have a "stop work" token which can be put into the queue. You then add as many tokens as you have threads. Alternatively, you have a separate flag to indicate that you want to stop. (This allows the other threads to stop before finishing all the current work in the queue.)
  • If the jobs are very small, consuming a single job at a time may not be the most efficient thing to do.

The ideas behind the code are more important than the code itself, to be honest.

这篇关于C#生产者/消费者的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆