C#中神器类BlockingCollection的实现详解

C#中神器类BlockingCollection的实现详解

什么是BlockingCollection

BlockingCollection 是 C# 中一个非常有用的线程安全的集合类,用于在多线程并发环境下进行数据的读取、写入和处理。它的用途非常广泛,比如在生产者-消费者模型中,用于协调生产者和消费者之间的数据传输,以及在大数据处理中,用于使用多个线程处理大规模的数据并发。

BlockingCollection的实现原理

BlockingCollection 是基于 ConcurrentQueue 实现的,底层用到了 Monitor.Wait()Monitor.Pulse() 进行线程同步。在集合为空时,调用 Take() 方法的线程会被阻塞,直到队列中有可用的数据;在队列满时,调用 Add() 方法的线程会被阻塞,直到队列有空闲的空间。

BlockingCollection的基本用法

创建BlockingCollection

在使用 BlockingCollection 之前,首先需要进行实例化:

var collection = new BlockingCollection<T>();

其中,T 为要存储的数据类型。

添加数据

可以使用 Add() 方法向 BlockingCollection 中添加数据:

collection.Add(data);

如果队列已满,Add() 方法会阻塞当前线程,直到队列有可用的空间。

取出数据

可以使用 Take() 方法从 BlockingCollection 中取出数据:

var data = collection.Take();

如果队列为空,Take() 方法会阻塞当前线程,直到队列中有可用的数据。

遍历数据

可以使用 foreach 循环遍历 BlockingCollection 中的数据:

foreach(var data in collection)
{
    // 处理数据
}

完成添加

在生产者向队列中添加完数据后,需要调用 CompleteAdding() 告知消费者已经完成添加操作:

collection.CompleteAdding();

判断是否有数据

可以使用 IsCompleted()Count 属性判断集合是否已经完成添加,以及队列中当前的元素个数:

if(collection.IsCompleted && collection.Count == 0)
{
    // 队列已经处理完毕
}

BlockingCollection的高级用法

设置队列的最大容量

可以在实例化 BlockingCollection 时指定队列的最大容量:

var collection = new BlockingCollection<T>(capacity);

设置超时等待

可以使用 Take()Add() 方法的带有超时参数的方法,来指定线程的等待时间:

var data = collection.Take(timeout);
collection.Add(data, timeout);

设置取消标志

可以使用 CancellationToken 对象来取消队列的操作,例如:

var cts = new CancellationTokenSource();

Task.Run(() =>
{
    while(!collection.IsAddingCompleted)
    {
        var data = collection.Take(cts.Token);
        // 处理数据
    }
});

// 取消操作
cts.Cancel();

示例1:生产者消费者模型

以下示例展示了 BlockingCollection 在生产者-消费者模型中的基本用法:

using System.Collections.Concurrent;
using System.Threading.Tasks;

class Program
{
    static void Main(string[] args)
    {
        var collection = new BlockingCollection<int>(5);

        // 生产者
        Task.Run(() =>
        {
            for (int i = 0; i < 10; i++)
            {
                collection.Add(i);
                Console.WriteLine($"生产者产生了数据{i},当前队列中元素个数为{collection.Count}");
            }
            // 完成添加
            collection.CompleteAdding();
        });

        // 消费者
        Task.Run(() =>
        {
            while (!collection.IsCompleted)
            {
                var data = collection.Take();
                Console.WriteLine($"消费者消费了数据{data},当前队列中元素个数为{collection.Count}");
                // 模拟耗时操作
                Thread.Sleep(1000);
            }
        });

        Console.Read();
    }
}

在上面的示例中,生产者不停地向队列中添加数据,每当添加完一个数字时就会打印出当前队列中元素个数;消费者不停地从队列中取出数据,并且每当取出一个数字时会打印出当前队列中元素个数。

当所有的数据都被生产者添加进队列中后,消费者就会停止取出数据,而由于生产者调用了 CompleteAdding() 方法,所以消费者不会再等待添加操作,从而退出循环。

示例2:并发任务处理

以下示例展示了如何使用 BlockingCollection 实现并发任务处理:

using System;
using System.Collections.Concurrent;
using System.Diagnostics;
using System.Threading.Tasks;

class Program
{
    static void Main(string[] args)
    {
        var collection = new BlockingCollection<int>();

        var sw = new Stopwatch();
        sw.Start();
        // 并发执行10个任务
        for (int i = 0; i < 10; i++)
        {
            var task = Task.Run(() =>
            {
                for (int j = 0; j < 1000000; j++)
                {
                    collection.Add(j);
                }
            });
        }

        Task.Run(() =>
        {
            while (!collection.IsCompleted)
            {
                var data = collection.Take();
                // 处理数据
            }
        }).Wait();

        sw.Stop();
        Console.WriteLine($"处理1亿条数据所需时间:{sw.ElapsedMilliseconds} ms");

        Console.Read();
    }
}

在上面的示例中,我们启动了10个并发任务,每个任务都向 BlockingCollection 中添加了100万条数字数据。最后,我们启动一个消费者任务,从队列中取出所有的数据,进行处理。运行结果表明,在多线程并发情况下,使用 BlockingCollection 去协调和同步数据的读取和写入,可以极大地提升程序的运行效率。

本站文章如无特殊说明,均为本站原创,如若转载,请注明出处:C#中神器类BlockingCollection的实现详解 - Python技术站

(0)
上一篇 2023年5月31日
下一篇 2023年5月31日

相关文章

  • C# Path.GetDirectoryName(string path):获取指定路径的目录路径

    Path.GetDirectoryName(string path)方法可以用于获取指定路径的目录名称。该方法会返回指定路径字符串中的目录信息。 使用方法: 要使用Path.GetDirectoryName方法,只需要将需要获取的文件路径作为参数传入该方法中即可。该方法可以是静态的,也可以是实例的。以下是代码示例: //静态方法 string directo…

    C# 2023年4月19日
    00
  • treeview递归绑定的两种方法

    下面是对 “treeview递归绑定的两种方法” 的详细解释: 标题 方法一 第一种方法是手动递归绑定treeview。我们可以用以下步骤来实现: 构造treeview,添加根节点。 设计递归函数,用于向treeview中添加子节点。 递归添加节点。 private void RecursiveAddToTreeView(TreeNode parentNod…

    C# 2023年5月31日
    00
  • 将DLL放入到资源中,运行时自动加载的小例子

    下面是将DLL放入到资源中,运行时自动加载的攻略: 1. 将DLL放入资源中 首先,我们需要将DLL文件放入资源中。 打开Visual Studio,创建一个普通的Win32控制台应用程序。 在解决方案资源管理器中,创建一个文件夹,用于存放DLL文件,例如命名为“DllRes”。 右键单击文件夹,选择“添加” -> “现有项”,将DLL文件添加到该文件…

    C# 2023年6月7日
    00
  • C#词法分析器之正则表达式的使用

    C#词法分析器之正则表达式的使用 前言 C#的正则表达式被广泛应用于文本处理和模式匹配。词法分析器是一个典型的例子,需要解析输入的字符串并将其转换为一个完整的语言结构。本文将介绍C#中正则表达式在词法分析器中的应用,并提供两个示例说明。 正则表达式基础 在开始说明C#中正则表达式在词法分析器中的应用之前,我们先来简单介绍正则表达式的基础知识。正则表达式由一些…

    C# 2023年6月7日
    00
  • c# Newtonsoft.Json 常用方法总结

    c# Newtonsoft.Json 常用方法总结 简介 Newtonsoft.Json 是一个高性能的 JSON 框架,为 JSON 互转提供了一系列便捷易用的 API,是 .NET 应用开发不可缺少的一部分。本文将介绍 Newtonsoft.Json 常用方法的总结,并且通过具体的示例进行说明,帮助读者更好的理解和应用。 安装 Newtonsoft.Js…

    C# 2023年5月31日
    00
  • DropDownList设置客户端事件思路

    下面是关于 DropDownList 设置客户端事件的完整攻略: 思路简介 DropDownList 是 ASP.NET WebForm 中常用的组件之一,在前端页面上展示一个下拉列表框,并且支持使用 C#、VB 等服务器端语言动态生成下拉列表内容。如果需要在前端页面使用 JS 代码对 DropDownList 进行操作,就需要用到客户端事件,这里主要指的是…

    C# 2023年5月31日
    00
  • c# 获取计算机硬件信息的示例代码

    这里提供一份C#获取计算机硬件信息的示例代码,可以使用System.Management命名空间中的ManagementObject类来获取计算机硬件信息。 步骤1:添加命名空间 首先,在代码文件中添加以下命名空间: using System.Management; 这个命名空间提供了可以获取WMI(Windows Management Instrument…

    C# 2023年5月31日
    00
  • websocket与C# socket相互通信

    web端代码就是js代码,C#有两种方式:使用第三方库,如Fleck,使用C#原生socket编程实现   web端: <!doctype html> <html lang=”zh-CN”> <head> <meta charset=”UTF-8″> <title>下发网站上文件到学生机</t…

    C# 2023年4月24日
    00
合作推广
合作推广
分享本页
返回顶部