-
2019-07-05 11:56:01
本文章仅为个人理解,如有错误请指正。
从.Net 4.0框架开始,在System.Collections.Concurrent命名空间下,增加了用于多线程协同的并发集合类(线程安全集合)。
线程安全集合:
就是当多线程访问时,采用了加锁的机制;即当一个线程访问集合时,会对这个集合进行保护,其他线程不能对其访问,直到该线程操作完集合之后,其他线程才可以使用。防止出现数据不一致或者数据被污染的情况。
只能保证集合的原子性、可见性,但是无法保证对集合操作的有序性,例如:多个线程同时将元素加入到集合,无法保证元素加入到集合的顺序,多次运行情况结果会不同。
BlockingCollection<T>
提供具有阻塞和限制功能的线程安全集合,实现IProducerConsumerCollection<T>接口。支持容量大小的限制和完成添加限制,当标记为完成添加后只允许移除(Take)操作,无法进行添加(Add)操作。
BoundedCapacity:获取集合限定容量,在通过构造函数时可以指定集合的容量大小。
IsAddingCompleted:判断是否已经标记完成添加,
IsCompleted:判断是否标记完成添加并且为空。
Add:将项添加到集合中。
CompleteAdding:将集合标记为完成添加。调用该函数后IsAddingCompleted为true,如果集合中包含的项数为0,那么IsCompleted也为true。
Take:从集合中移除一项。
TryAdd:尝试将项加入集合
TryTake:尝试从集合中移除项。
ConcurrentBag<T>
提供可供多个线程同时安全访问的无序包。对应非泛型列表List<T>。
包(Bag)和数据上的集(Set)的区别是包可包含重复元素,而集中不能包含重复元素。
Count:获取无序包中的元素数量
IsEmpty:判断无序包是否为空。
TryPeek:从无序包中获取一个元素,但不进行移除。
TryTask:从无序包获取一个元素并移除。
ConcurrentDictionary<TKey,TValue>
提供可供多线程同时访问的键值对的线程安全集合,对应Dictionary<TKey, TValue>。
IsEmpty:判断字典是否为空。
AddOrUpdate:将键值对添加到字典中,如果Key值已经存在,则更新Value值。
Clear:将所有键值对从字典中移除。
GetOrAdd:如果键不存在,则将键值对添加到字典中并返回Value值,如果键已经存在,则返回现有值,不会更新现有值。
TryUpdate:尝试更新键对应的Value值。
ConcurrentQueue<T>
队列,提供线程安全的先进先出(FIFO)集合,对应Queue<T>。
Enqueue:将对象添加到队列末尾处。
TryDequeue:尝试移除并返回位于队列开头处的对象。
TryPeek:尝试返回队列开头处的对象但不将其移除。
ConcurrentStack<T>
栈,提供线程安全的后进先出(LIFO)集合,对应Stack<T>
Push:将对象插入栈顶部。
PushRange:将多个对象插入栈的顶部。
TryPeek:尝试返回栈顶部的对象但不将其移除。
TryPop:尝试弹出并返回栈顶部的对象。
TryPopRange:尝试弹出并返回栈顶部的多个对象。
更多相关内容 -
c#官方线程安全集合源码
2018-03-27 10:36:48c#官方线程安全集合源码,concurrentBag concurrentqueue,concurrentset,concurrentDictionary,concurrentSet等 -
C#线程安全集合ConcurrentDictionary
2020-11-25 10:15:26C#线程安全集合ConcurrentDictionary 这个在System.Collections.Concurrent的字典类跟字典Dictionary的使用差不多是一样的,但在多线程并发访问的时候,只能靠ConcurrentDictionary了。 这是我之前储存多个...C#线程安全集合ConcurrentDictionary
这个在System.Collections.Concurrent的字典类跟字典Dictionary的使用差不多是一样的,但在多线程并发访问的时候,只能靠ConcurrentDictionary了。
这是我之前储存多个SineTones的频率范围使用Dictionary用多线程并发访问:
public static Dictionary<double, Tuple<double, double>> FreqRange { get; set; } public static void CalcSineTones() { FreqRange = new Dictionary<double, Tuple<double, double>>(); Parallel.ForEach(OrderTrackDicResult, pair => { SineTonesDic[pair.Key] = CalcAmp(pair.Key, pair.Value); SineTonesDicResult[pair.Key] = SineTonesDic[pair.Key]; }); } private static void CalcAmp(double order, double[][] otrkBuffer) { double minfreq,maxfreq; GetMaxMinFreq(order, otrkBuffer[0], out minfreq, out maxfreq); FreqRange[order] = new Tuple<double, double>(minfreq, maxfreq); }
结果报System.NullRefrenceException错误:
将Dictionary替换为ConcurrentDictionary:
public static ConcurrentDictionary<double, Tuple<double, double>> FreqRange { get; set; } public static void CalcSineTones() { FreqRange = new ConcurrentDictionary<double, Tuple<double, double>>(); Parallel.ForEach(OrderTrackDicResult, pair => { SineTonesDic[pair.Key] = CalcAmp(pair.Key, pair.Value); SineTonesDicResult[pair.Key] = SineTonesDic[pair.Key]; }); } private static void CalcAmp(double order, double[][] otrkBuffer) { double minfreq,maxfreq; GetMaxMinFreq(order, otrkBuffer[0], out minfreq, out maxfreq); FreqRange[order] = new Tuple<double, double>(minfreq, maxfreq); }
运行没有问题!
-
C#线程安全的集合
2020-06-15 18:17:48表示对象的线程安全的无序集合。 static void Main(string[] args) { ConcurrentBag<int> thList = new ConcurrentBag<int>(); Parallel.For(0, 100000, a => { thList.Add(a); }); thList....ConcurrentBag 集合
表示对象的线程安全的无序集合。
static void Main(string[] args) { ConcurrentBag<int> thList = new ConcurrentBag<int>(); Parallel.For(0, 100000, a => { thList.Add(a); }); thList.TryPeek(out int result1); //获取末尾的值 Console.WriteLine(string.Format("Count:{0} Result:{1}", thList.Count, result1)); thList.TryTake(out int result2); //删除并获取末尾的值 Console.WriteLine(string.Format("Count:{0} Result:{1}", thList.Count, result2)); Console.ReadKey(); }
注:若是使用List<int>,在并行添加数据时要么会抛异常要么集合中的个数不对。
PS:ConcurrentBag<T>不能像List<T>一样轻易获取任何索引处的值和删除任意一个值。 若要使用获取和删除,请使用ConcurrentDictionary。
ConcurrentDictionary 字典
static void Main(string[] args) { ConcurrentDictionary<int, int> thDict = new ConcurrentDictionary<int, int>(); Parallel.For(0, 10, a => { bool isOK = thDict.TryAdd(a, a); if (thDict.ContainsKey(4)) { thDict[4] = 400; } }); Console.WriteLine(string.Format("Count:{0} Values:{1}", thDict.Count, string.Join(",", thDict.Values))); thDict.TryRemove(3, out int value); Console.WriteLine(string.Format("Count:{0} value:{1} Values:{2}", thDict.Count, value, string.Join(",", thDict.Values))); Console.ReadKey(); }
ConcurrentQueue 队
表示线程安全的先进先出 (FIFO) 集合。
static void Main(string[] args) { ConcurrentQueue<int> thQueue = new ConcurrentQueue<int>(); Parallel.For(0, 10, a => { thQueue.Enqueue(a); }); thQueue.TryPeek(out int result1); //获取开头处的对象 thQueue.TryDequeue(out int result2); //获取开头处的对象并将其移除 Console.ReadKey(); }
ConcurrentStack 栈
表示线程安全的后进先出 (LIFO) 集合。
static void Main(string[] args) { ConcurrentStack<int> thQueue = new ConcurrentStack<int>(); Parallel.For(0, 10, a => { thQueue.Push(a); }); thQueue.TryPeek(out int result1); //获取顶部的对象 thQueue.TryPop(out int result2); //获取顶部的对象并将其移除 Console.ReadKey(); }
-
C#线程安全集合类说明(1): BlockingCollection<T>
2020-10-16 15:31:23线程安全的集合所在的命名空间 using System.Collections.Concurrent; 相应的类: BlockingCollection<T> ConcurrentBag<T> ConcurrentDictionary<Tkey, TValue> ConcurrentQueue<T> ...线程安全的集合所在的命名空间 using System.Collections.Concurrent;
Concurrent意思是并发的,并行的。反义是sequential(顺序的),线程安全的意思就是多线程中的同步
System.Collections.Concurrent 命名空间
System.Collections.Concurrent
命名空间提供多个线程安全集合类。当有多个线程并发访问集合时,应使用这些类代替 System.Collections 和 System.Collections.Generic 命名空间中的对应类型。 但是,不保证通过扩展方法或通过显式接口实现访问集合对象是线程安全的,可能需要由调用方进行同步。类
类 BlockingCollection<T> 为实现 IProducerConsumerCollection<T> 的线程安全集合提供阻塞和限制功能。
ConcurrentBag<T> 表示对象的线程安全的无序集合。
ConcurrentDictionary<TKey,TValue> 表示可由多个线程同时访问的键/值对的线程安全集合。
ConcurrentQueue<T> 表示线程安全的先进先出 (FIFO) 集合。
ConcurrentStack<T> 表示线程安全的后进先出 (LIFO) 集合。
OrderablePartitioner<TSource> 表示将可排序数据源拆分为多个分区的特定方式。
Partitioner 提供针对数组、列表和可枚举项的常见分区策略。
Partitioner<TSource> 表示将数据源拆分为多个分区的特定方式。
接口
接口 IProducerConsumerCollection<T> 定义供制造者/使用者用来操作线程安全集合的方法。 此接口提供一个统一的表示(为生产者/消费者集合),从而更高级别抽象如 BlockingCollection<T> 可以使用集合作为基础的存储机制。
枚举
枚举 EnumerablePartitionerOptions 指定控制分区程序的缓冲行为的选项。
参考微软官方文档:
https://docs.microsoft.com/zh-cn/dotnet/api/system.collections.concurrent?view=netframework-4.5
参考源代码(类BlockingCollection<T>)
一、示例:BlockingCollection<T> ,该类与队列ConcurrentQueue<T>类似。
源程序:
using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading;
using System.Threading.Tasks;namespace ThreadSafeCollectionDemo
{
class Program
{
static void Main(string[] args)
{
Console.WriteLine("测试阻塞集合BlockingCollection的添加【Add】和移除【Take】...");
Task task = AddTakeBlockingCollectionAsync();
Task.WaitAll(task);
Console.WriteLine("下面测试阻塞集合BlockingCollection的尝试移除【TryTake】...");
TryTakeBlockingCollection();
Console.ReadLine();
}/// <summary>
/// 阻塞集合BlockingCollection的Add(添加)和Take(移除):生产者【添加元素】、消费者【移除元素】线程安全同时操作
/// </summary>
/// <returns></returns>
static async Task AddTakeBlockingCollectionAsync()
{
using (BlockingCollection<int> blockingCollection = new BlockingCollection<int>())
{
//生产者
Task taskProducer = Task.Run(() =>
{
//添加一个元素
blockingCollection.Add(1);
blockingCollection.Add(2);
blockingCollection.Add(3);
//结束添加元素:集合已标记为完成添加。CompleteAdding()方法会导致Take()方法出现移除操作异常
blockingCollection.CompleteAdding();
});
//消费者
Task taskConsumer = Task.Factory.StartNew(() =>
{
try
{
while (true)
{
//移除一个元素,并返回这个移除的元素
int removeItem = blockingCollection.Take();
Console.WriteLine(removeItem);
}
}
catch (Exception ex)
{
Console.WriteLine($"移除项出现异常:{ex.Message}");
}
});
await Task.WhenAll(taskProducer, taskConsumer);
}
}/// <summary>
/// 尝试移除阻塞集合BlockingCollection中的元素
/// </summary>
static void TryTakeBlockingCollection()
{
using (BlockingCollection<int> bc = new BlockingCollection<int>())
{
for (int i = 0; i < 1000; i++)
{
bc.Add(i);
}
Console.WriteLine($"是否完成添加:【{bc.IsAddingCompleted}】");
bc.CompleteAdding();//标记集合已经完成添加
Console.WriteLine($"是否完成添加:【{bc.IsAddingCompleted}】");int outerSum = 0;
Action action = new Action(() =>
{
int localItem;
int sum = 0;
while (bc.TryTake(out localItem))
{
sum += localItem;
}
Interlocked.Add(ref outerSum, sum);
Console.WriteLine($"求和Sum={sum},当前替换:【{outerSum}】");
});//并行操作:三个委托同时执行
//相当于 将数组求和 分 三小部分数组 分别求和,最后合并
Parallel.Invoke(action, action, action);
Console.WriteLine($"此时outerSum为【{outerSum}】");
Console.WriteLine($"此时集合已完成添加并且集合为空:【{bc.IsCompleted}】");
}
}}
}程序运行如图:
-
C#线程安全集合类说明(2): ConcurrentBag<T>
2020-10-16 17:06:42线程安全的集合所在的命名空间 using System.Collections.Concurrent; Concurrent意思是并发的,并行的。反义是sequential(顺序的),线程安全的意思就是多线程中的同步锁(相当于增加了lock或Interlocked) -
C#多线程List的非线程安全性
2018-11-07 11:30:41最近在做多线程方面的工作,工作中发现多线程中很多坑,这里就有一个List添加对象的误区,解释了List容量扩展后内存开辟导致的线程不安全性的问题,这里做个分享跟大家讲讲这个坑是怎么形成的怎么样避免。 -
C#多线程下的线程安全集合使用
2019-09-14 04:31:07多线程下的集合操作分析 转载于:https://my.oschina.net/u/2475253/blog/3065165 -
C# 4.0 之线程安全集合篇
2017-11-01 15:04:22而集合作为一种重要的临界资源,通用性更广,为了让大家更安全的使用它们,微软为我们带来了强大的并行集合:System.Collections.Concurrent里面的各位仁兄们。 首先,咱们从一个经典的问题谈起。 生产者消费... -
C# 各种常用集合类型的线程安全版本
2019-03-23 13:26:45在C#里面我们常用各种集合,数组,List,Dictionary,Stack等,然而这些集合都是非线程安全的,当多线程同时读写这些集合的时候,有可能造成里面的数据混乱,为此微软从Net4.0开始专门提供了另一套线程安全的版本(放在System... -
C#线程例子集合
2018-08-08 11:11:32整合了各种对线程使用的场景例子,多线程互斥,Http通信 -
C#的变迁史 - C# 4.0 之线程安全集合篇
2017-01-17 10:08:01C#的变迁史 - C# 4.0 之线程安全集合篇 作为多线程和并行计算不得不考虑的问题就是临界资源的访问问题,解决临界资源的访问通常是加锁或者是使用信号量,这个大家应该很熟悉了。 而集合作为一种重要的临界资源... -
C#多线程处理List集合数据
2022-01-27 08:44:29List集合是非线程安全的,所以我们采用并行编程时会发生错误。如下图所示 Parallel.For(0, 1000, (i) => { Product product = new Product(); product.Name = "name" + i; product.Category = "Category" ... -
C# 线程安全
2021-09-15 20:58:56System.Collections.Concurrent 命名空间下提供多个线程安全集合类,只要多个线程同时访问集合,就应使用这些类来代替 System.Collections 和 System.Collections.Generic 命名空间中的相应类型。 但是,不保证通过... -
C# 中的线程安全集合类
2017-10-12 14:28:26对于ArrayList以及Hashtable 集合类来讲,当需要做到线程安全的时候,最好利用其自带的属性SyncRoot 来做到,尽管也可以使用其Synchronized()方法来实现,但是使用属性会更好。 线程安全集合: ... -
C#线程安全集合类说明(3): ConcurrentDictionary,TValue>字典
2020-10-17 16:08:31线程安全的集合所在的命名空间 using System.Collections.Concurrent; Concurrent意思是并发的,并行的。反义是sequential(顺序的),线程安全的意思就是多线程中的同步锁(Lock) -
C#线程安全类型解析及用法
2020-04-20 00:07:16在C#里面我们常用各种集合、数组、List、Dictionary、Stack等,然而这些集合都是非线程安全的,当多线程同时读写这些集合的时候,有可能造成里面的数据混乱,为此微软从Net4.0开始专门提供了另一套线程安全类型,... -
C# 并发安全集合ConcurrentBag取代List
2018-06-01 18:19:32List集合是非线程安全的,所以我们这里了解下安全集合ConcurrentBag。控制台测试程序:using System; using System.Collections.Concurrent; using System.Collections.Generic; using System.Diagnostics; using ... -
c#集合类的线程安全(整理)
2013-03-13 13:52:30QueueT> 类 MSDN的说法 线程安全 ...此类型的公共静态(在 Visual Basic ...即便如此,从头到尾对一个集合进行枚举本质上并不是一个线程安全的过程。 若要确保枚举过程中的线程安全,可以在整个枚举过程中锁定 -
C# 线程安全问题
2020-07-03 00:04:47什么是线程安全 如果你的代码在进程中有多个线程同时运行一段代码,如果每次执行的结果都和单线程运行时的结果一致, 那么就是线程安全的 先看下面两段代码,执行结果是否一样? int num1 = 0; int num2 = 0; for ... -
C# 多线程资料集合
2010-10-12 14:32:20C#多线程资料集合:包括编程实例实战,线程技术,多线程的相关概念,ASP.Net多线程编程等内容。是学习C#线程编程技术的良师益友! -
C#线程安全
2017-07-18 15:33:24鄙人在网上找了很多关于线程安全的资料,看了很久还是有点不知所云的感觉。今天勉强有了一点肤浅的理解,所以记录下来。 一、什么是线程安全 首先看一段百度百科的解释:线程安全就是多线程访问时,采用了加锁机制... -
c#集合类的线程安全
2017-09-07 15:39:02原文网址:... 即位于System.Collections命名空间下的集合,如Hashtable,ArrayList,Stack,Queue等.其均提供了线程同步的一个实现 ...集合线程同步的问题 public class Demo8 { Array -
c# 实现线程安全的List容器
2019-12-27 16:17:49c#实现线程安全的List,主要还是给夹锁。代码如下: using System; using System.Collections; using System.Collections.Generic; using System.Linq; using System.Runtime; using System.Text; namespace ... -
【原创】C#List线程安全相关解决方案
2022-03-03 10:16:57在多线程程序中,可能多个线程都需要操作一个共用的List,而微软实现的List却是非线程安全的。也就是说,在多线程情况下,共用的List会产生线程安全问题。 对于这个问题,有不少的解决方案,比如换用线程安全的类型... -
C#线程入门教程之单线程介绍
2021-01-01 14:15:49一个标准的线程由线程ID,当前指令指针(PC),寄存器集合和堆栈组成。 什么是多线程? 多线程是指程序中包含多个执行流,即在一个程序中可以同时运行多个不同的线程来执行不同的任务,也就是说允许单个程序创建多个...