`
wyf
  • 浏览: 433026 次
  • 性别: Icon_minigender_1
  • 来自: 唐山
社区版块
存档分类
最新评论

BlockingCollection 和 IProducerConsumerCollection

    博客分类:
  • C#
阅读更多

1、BlockingCollection 为实现 IProducerConsumerCollection<T> 的线程安全集合提供阻塞和限制功能。

实现了 IProducerConsumerCollection<T> 接口的有:ConcurrentStack, ConcurrentQueue, and ConcurrentBag。

如果初始化BlockingColloction的时候不指定IProducerConsumerCollection<T>。则默认为ConcurrentQueue (FIFO) 。

2、为了展示BlockingCollection类的主要功能,让我们看看一个多线程场景,我们先用一个普通集合类。假设我们有一个队列,我们​​希望将工作放入该队列,并让其他线程消耗放入队列的项目,以便对它们执行某些操作。

 

var queue = new Queue<string>();
Task.Factory.StartNew(() =>
{
    while (true)
    {
        queue.Enqueue("value");
    }
});

 在这个例子中,生成任务一直向队列中添加一个值value

Task.Factory.StartNew(() =>
{
    while (true)
    {
        if (queue.Count > 0)
        {
            string value = queue.Dequeue();
            Console.WriteLine("Worker 1: " + value);
        }
    }
    
});

Task.Factory.StartNew(() =>
{
    while (true)
    {                    
        if (queue.Count > 0)
        {
//并发异常
            string value = queue.Dequeue();
            Console.WriteLine("Worker 2: " + value);
        }
    }
    
});

 开启两个消费任务来消费队列中的值。我们会发现,高负载运行的时候,其中一个任务检查队列是否为空,然后去拉取数值,这个时候另一个线程可能已经删除了值。显然会抛出异常。

  那么,为了支持这种情况,我们需要做些什么呢?一种方法是使用lock关键字序列化对队列的所有调用。如果每个工作项都很重,这可能是一个好的解决方案,但如果我们的工作项很轻或我们有大量的消费者,那么它会破坏性能。好吧,在.NET 4.0中,我们可以使用几种无锁数据结构。实际上,这是一个并发队列。如果我们使用ConcurrentQueue,我们可以编写如下代码:

 

var queue = new ConcurrentQueue<string>();
Task.Factory.StartNew(() =>
{
    while (true)
    {
        queue.Enqueue("value" + count);
        count++;                    
    }
});

Task.Factory.StartNew(() =>
{
    while (true)
    {
        string value;
        if (queue.TryDequeue(out value))
        {
            Console.WriteLine("Worker 1: " + value);
        }
    }
});

Task.Factory.StartNew(() =>
{
    while (true)
    {
        string value;
        if (queue.TryDequeue(out value))
        {
            Console.WriteLine("Worker 2: " + value);
        }
        
    }
});

 

这很不错,但是如果我们可以尝试获取一个项目并且如果没有可用的项目就阻塞队列,那会不会很好?当然会的!这就是我们拥有BlockingCollection的原因。它实现了这种确切的行为,还有一些额外的行为。BlockingCollection在其构造函数中采用IProducerConsumerCollection,或者如果调用其空构造函数,它将默认使用ConcurrentQueue。然后你要做的就是在BlockingCollection上调用“Add”或“Take”,如果队列中没有任何内容,它将阻止。所以上面的代码看起来像这样:

 

var blockingCollection = new BlockingCollection<string>();
Task.Factory.StartNew(() =>
{
    while (true)
    {
        blockingCollection.Add("value" + count);
        count++;                    
    }
});

Task.Factory.StartNew(() =>
{
    while (true)
    {                    
        Console.WriteLine("Worker 1: " + blockingCollection.Take());
    }
});

Task.Factory.StartNew(() =>
{
    while (true)
    {
        Console.WriteLine("Worker 2: " + blockingCollection.Take());
    }
});

 这已经很好了,但是我们还是会有While声明,我们不用这些while可不可以呢?当然可以啦!BlockingCollection还为我们实现了这种行为,并使用了一个名为“GetConsumingEnumerable”的方法。我们所做的只是调用此方法,然后迭代生成的IEnumerable并阻塞,直到找到工作项!现在这很好!所以上面的一个消费者看起来像这样:

 

Task.Factory.StartNew(() =>
{
    foreach (string value in blockingCollection.GetConsumingEnumerable())
    {
        Console.WriteLine("Worker 1: " + value);
    }                
});

 这段代码将永远存在,迭代阻塞集合,并在项目用完时阻塞。一旦新项目开始出现在集合中,它将再次开始枚举它们!很容易!

      BlockingCollection的作用就是让一些线程生成数据,并让许多其他线程获取和处理相同数据的非常简单的方法。您可以切换其底层存储机制,以便影响项目在添加项目时的行为,以及数据项来自哪里,何时从中获取数据项,所有数据项都完全从基础数据存储中抽象出来。我希望你找到BlockingCollection类的一些很好的用途,我希望你喜欢这篇文章!

 

 

分享到:
评论

相关推荐

    .NetCore利用BlockingCollection实现简易消息队列

    BlockingCollection是基于ConcurrentQueue和ConcurrentStack实现的,这两个类都提供了线程安全的插入和删除操作。当尝试从空队列中获取元素时,Take方法会阻塞,直到有新的元素添加;而Add方法在队列满时也会阻塞,...

    生产者消费者问题C#

    通过使用`BlockingCollection`,我们可以轻松地管理生产者和消费者的同步,避免了繁琐的锁和信号量操作。在实际编程中,可以根据需求调整生产者和消费者的数量,以及数据处理的逻辑,以适应各种应用场景。

    线程安全集合类Concurrent测试

    Task task = BlockingCollectionUtil.AddTakeBlockingCollectionAsync(); Task.WaitAny(task); BlockingCollectionUtil.TryTakeBlockingCollection(); ConcurrentBagUtil.Test(); ConcurrentDictionaryUtil....

    C#并发集合的简单方法.pdf

    5. **BlockingCollection**:阻塞集合,它是一个抽象类,可以作为基础构建块来创建自定义的并发集合,提供添加和获取元素时的阻塞功能。 使用这些并发集合,开发者可以在编写多线程代码时减少对同步原语的依赖,...

    进程同步(c#实现)

    C#作为一门广泛应用于Windows平台的编程语言,提供了丰富的机制来处理进程同步问题,以确保资源的正确访问和避免数据竞争。这里我们将深入探讨如何用C#实现生产者消费者问题,这是一种典型的进程同步场景。 生产者...

    .NET Framework4 线程安全集合详解

    - **概述**:BlockingCollection&lt;T&gt; 是一个高级的线程安全集合,实现了 `IProducerConsumerCollection&lt;T&gt;` 接口。它提供了线程间的生产者-消费者模式,支持添加和移除操作的阻塞,可以限制集合的大小,避免资源过度...

    C#任务队列的实现

    它通过将任务放入队列中,然后由一个或多个工作线程按顺序取出并执行,有效地实现了任务的异步处理和调度。下面将详细讨论如何实现C#任务队列及其相关知识点。 1. **线程安全**: - 在多线程环境下,对任务队列的...

    C#写的生产者消费者问题

    这个命名空间提供了诸如`Monitor`、`Mutex`、`Semaphore`和`SemaphoreSlim`等同步原语,以及`Task`和`BlockingCollection`等高级并发工具,可以帮助我们解决这个问题。 首先,我们需要一个数据结构来充当仓库,存储...

    C#生产者消费者模拟

    此外,`Task`和`BlockingCollection`也是实现并发处理的常用工具,它们提供了线程安全的数据结构和任务调度功能。 在本项目中,`Production.sln`是Visual Studio的解决方案文件,包含了整个项目的配置信息和依赖...

    ThreadSynchronization.zip

    在编程领域,尤其是在多核处理器系统中,多线程已经成为了一种常见的编程模式,用于提高程序的并发性和执行效率。本文将围绕“C#多线程”中的一个重要概念——线程同步,结合“ThreadSynchronization.zip”压缩包中...

    .net 多线程 模拟生产者消费者(WindForm版)

    4. **线程间通信**:`BlockingCollection`提供了`TryAdd`和`Take`方法,用于生产者添加元素和消费者取出元素,它们在队列为空或满时会自动阻塞相应的线程,实现了生产者和消费者之间的同步。 5. **异常处理**:在多...

    生产者消费者为模型的多线程编程c#原创

    例如,`Monitor`类用于线程同步,`Semaphore`或`SemaphoreSlim`用于控制资源访问数量,`Task`和`Task.Run`用于异步操作,`ConcurrentQueue`或`BlockingCollection`作为线程安全的队列来存储数据。 在"生产者消费者...

    producers-and-consumers.zip_C# 生产者_c# 生产 消费者_c#消费者_c#生产消费_neckq

    3. **队列**:通常用作生产者和消费者之间的数据缓冲区,C#中的`ConcurrentQueue&lt;T&gt;`或`BlockingCollection&lt;T&gt;`是线程安全的队列,适合这种场景。 4. **WaitOne() 和 Pulse()**:`Monitor`类的`WaitOne()`方法可以...

    生产者消费者问题和猴子过桥问题源代码

    此外,`BlockingCollection&lt;T&gt;`是一个线程安全的数据结构,可以作为共享缓冲区,它提供了添加和移除元素的线程安全操作。 猴子过桥问题,又称为“聪明的小猴”或“猴子搬桃”问题,是一个智力游戏,旨在考察解决...

    可重复使用的并行数据结构和算法.pdf

    .NET框架中的`System.Collections.Concurrent.BlockingCollection&lt;T&gt;`提供了这个功能,它可以限制缓冲区的大小,防止资源耗尽。 5. Thin 事件: Thin事件是轻量级的事件触发机制,用于线程间的通信,比传统的.NET...

    Pipeline管道

    用C#实现管道,用BlockingCollection 集合。线程安全

    生产消费者队列(c#),用于线程的队列自动同步

    在多线程编程中,生产者消费者模型是一种常见的设计模式,用于解决线程间的通信和同步问题。在C#中,我们可以利用各种机制实现这样的队列。本篇将详细讲解如何在C#中构建一个生产消费者队列,以及它如何帮助优化线程...

    CsGo并发流程控制框架

    在`ConsoleTest`中,我们可能看到使用`BlockingCollection`实现生产者消费者模型的例子,其中`Add`和`Take`方法分别用于添加任务和取出任务执行,实现任务队列的并发访问。 六、线程池 线程池是一种高效的线程管理...

    【原创】C#多线程_跨线程窗体同步_进度条_生产消费(源代码)

    在C#编程中,多线程技术是一种提升应用程序性能的重要手段。它允许程序同时执行多个任务,从而充分利用多核...通过研究和理解这个项目,开发者可以更好地掌握如何在实际项目中应用这些技术,提高程序的性能和用户体验。

Global site tag (gtag.js) - Google Analytics