《C#并行编程高级教程》第4章 并发集合 笔记

这一章主要介绍了System.Collections.Concurrent下的几个类。

ConcurrentQueue<T>

并发队列。完全无锁,使用CAS(compare-and-swap)比较并交换和自旋重试来实现线程安全。
//加入队尾
public void Enqueue(T item)
//尝试删除队头,并将元素通过out返回,返回值表示是否操作成功
public bool TryDequeue( out T result)
//尝试获取队头,通过out返回元素,返回值为代表是否操作成功
public bool TryPeek( out T result)

ConcurrentStack<T>

并发栈,完全无锁,使用CAS(compare-and-swap)比较并交换和自旋重试来实现线程安全。
 
public void Push(T item)
public void PushRange(T[] items)
public void PushRange(T[] items, int startIndex, int count)
 
public bool TryPeek( out T result)
 
public bool TryPop( out T result)
public int TryPopRange(T[] items)
public int TryPopRange(T[] items, int startIndex, int count)

ConcurrentBag<T>

这是一个无序的对象集合,而且支持对象重复。在同一线程下添加和删除效率高,又是需要锁。
public void Add(T item)
public bool TryPeek( out T result)
public bool TryTake( out T result)

ConcurrentDictionary<TKey,TValue>

并发的键值对,读是没有锁的,写会有细粒度的锁。
 
public TValue GetOrAdd(TKey key,TValue value)
public bool TryAdd(TKey key,TValue value)
public bool TryGetValue(TKey key, out TValue value)
public bool TryRemove(TKey key, out TValue value)
public bool TryUpdate(TKey key,TValue newValue,TValue comparisonValue)

IProducerConsumer<T>与BlockingCollection<T>

IProducerConsumerCollection<T>是对生产者-消费者模型的一个操作抽象,BlockingCollection<T>是一个实现。
针对生产者消费者模型,给出了很多方便的功能。
构造的时候可以设置最大容量,当集合达到最大容量,添加请求会被阻塞。
添加完成的判断也不需要自己在维护标识符
更有GetConsumingEnumerable(),获取集合迭代器,可以使用foreach,如果集合为空着会阻塞,并等待添加新的元素,如果集合没有元素并且IsAddingCompleted为true那么循环终止,可以省去不必要的自旋。
还支持超时和取消功能
 
private const int NUM_SENTENCES = 2000000;
private static BlockingCollection < string > _sentencesBC;
private static BlockingCollection < string > _capWordsInSentencesBC;
private static BlockingCollection < string > _finalSentencesBC;
 
private static void ProduceSentences(System.Threading.CancellationToken ct)
{
     //...
     if ( !_sentencesBC.TryAdd(newSentence, 2000, ct))
    {
         throw new TimeoutException(...);
    }
     //...
    _sentencesBC.CompleteAdding();
}
 
private static void CapitalizeWordsInSentences()
{
     //...
     while ( !_sentencesBC.IsCompleted)
    {
         string sentence;
         if (_sentencesBC.TryTake( out sentence))
        {
            _capWordsInSentencesBC.Add(...);
        }
    }
    _capWordsInSentencesBC.CompleteAdding();
}
 
private static void RemoveLettersInSentences()
{
     //...
     foreach (var sentence in _capWordsInSentencesBC.GetConsumingEnumerable())
    {
        _finalSentencesBC.Add(RemoveLetters(letterChars, sentence));
    }
    _finalSentencesBC.CompleteAdding();
}
 
static void Main( string[] args)
{
    _sentencesBC = new BlockingCollection < string >(NUM_SENTENCES);
    _capWordsInSentencesBC = new BlockingCollection < string >(NUM_SENTENCES);
    _finalSentencesBC = new BlockingCollection < string >(NUM_SENTENCES);
 
    var cts = new System.Threading.CancellationTokenSource();
    var ct = cts.Token;
    var deferredCancelTask = Task.Factory.StartNew(() = >
    {
        System.Threading.Thread.Sleep( 500);
        cts.Cancel();
    });
 
    Parallel.Invoke(
        () = > ProduceSentences(ct),
        () = > CapitalizeWordsInSentences(),
        () = > RemoveLettersInSentences()
        );
}

任务计数器

常常需要知道还在运行的Task的数量。所以需要对计数器进行原子的加减
可以在任务新建的时候使用System.Threading.Interlocked.Increment(ref tasksRunning)
在任务结束后System.Threading.Interlocked.Decrement(ref tasksRunning);
private static int tasksRunning = 0;
 
//inti method
for ( int i = 0; i < taskMax; i ++)
{
    System.Threading.Interlocked.Increment( ref tasksRunning);
    tasks[i] = Task.Factory.StartNew(() = >
    {
         try
        {
             //...
        }
         finally
        {
            System.Threading.Interlocked.Decrement( ref tasksRunning);
        }
    });
}
 
//other method
while ((tasksRunning > 0))
{
     //...
}

并发集合和不安全集合互转

并发集合可以结构一个IEnumerable接口的集合做构造函数参数。
例如
string[] _invalidHexValues = { "AF", "BD", "BF", "CF", "DA", "FA", "FE", "FF" };
var invalidHexValuesStack = new ConcurrentStack < string >(_invalidHexValues);
要把并发集合转成不安全的可以使用CopyTo ToArray等方法

volatile关键字

使用volatile可以确保在不同的线程中进行访问的时候,可以得到最新值。这些变量不会被编译器按照只在一个线程中的进行访问的假定进行优化。
private static volatile bool _flag = false;
 

 



转载于:https://www.cnblogs.com/atskyline/p/3234805.html

表情包
插入表情
评论将由博主筛选后显示,对所有人可见 | 还能输入1000个字符
相关推荐
第1 基于任务的程序设计 1.1使用共享内存的多核系统 1.1.1共享内存多核系统与分布式内存系统之间的区别 1.1.2并行程序设计和多核程序设计 1.2理解硬件线程和软件线程 1.3理解Amdahl法则 1.4考虑Gustafson法则 1.5使用轻量级并发模型 1.6创建成功的基于任务的设计 1.6.1以并发的思想指导设计 1.6.2理解交错并发并发并行之间的区别 1.6.3并行化任务 1.6.4尽量减少临界区 1.6.5理解多核并行程序的设计原则 1.7为NUMA架构和更高的可扩展性做好准备 1.8判断是否适合并行化 1.9小结 第2 命令式数据并行 2.1加载并行任务 2.1.1 System.Threading.Tasks.Parallel类 2.1.2 Parallel.Invoke 2.2将串行代码转换为并行代码 2.2.1检测可并行化的热点 2.2.2测量并行执行的加速效果 2.2.3理解并发执行 2.3循环并行化 2.3.1 Parallel.For 2.3.2 Parallel.ForEach 2.3.3从并行循环中退出 2.4指定并行度 2.4.1 ParallelOptions 2.4.2计算硬件线程 2.4.3逻辑内核并不是物理内核 2.5通过甘特图检测临界区 2.6小结 第3 命令式任务并行 3.1创建和管理任务 3.1.1 System.Threading.Tasks. Task 3.1.2理解Task状态和生命周期 3.1.3通过使用任务来对代码进行并行化 3.1.4等待任务完成 3.1.5忘记复杂的线程 3.1.6通过取消标记取消任务 3.1.7从任务返回值 3.1.8 TaskCreationOptions 3.1.9通过延续串联多个任务 3.1.10编写适应并发并行的代码 3.2小结 第4 并发集合 4.1理解并发集合提供的功能 4.1.1 System.CollectionsConcurrent 4.1.2 ConcurrentQueue 4.1.3理解并行的生产者⋯ 消费者模式 4.1.4 ConcurrentStack 4.1.5将使用数组和不安全集合的代码转换为使用并发集合的代码 4.1.6 ConcurrentBag 4.1.7 IProducerConsumerCollection 4.1.8 BlockingCollection 4.1.9 ConcurrentDictiona 4.2小结 第5 协调数据结构 5.1通过汽车和车道理解并发难题 5.1.1非预期的副作用 5.1.2竞争条件 5.1.3死锁 5.1.4使用原子操作的无锁算法 5.1.5使用本地存储的无锁算法 5.2理解新的同步机制 5.3使用同步原语 5.3.1通过屏障同步并发任务 5.3.2屏障和ContinueWhenAll 5.3.3在所有的参与者任务中捕捉异常 5.3.4使用超时 5.3.5使用动态数目的参与者 5.4使用互斥锁 5.4.1使用Monitor 5.4.2使用锁超时 5.4.3将代码重构为避免使用锁 5.5将自旋锁用作互斥锁原语 5.5.1使用超时 5.5.2使用基于自旋的等待 5.5.3自旋和处理器出让 5.5.4使用volatile修饰符 5.6使用轻量级的手动重置事件 5.6.1使用ManualResetEventSlim进行自旋和等待 5.6.2使用超时和取消 5.6.3使用ManualResetEvent 5.7限制资源的并发访问 5.7.1使用SemaphoreSlim 5.7.2使用超时和取消 5.7.3使用Semaphore 5.8通过CountdownEvent简化动态fork和join场景 5.9使用原子操作 5.10小结 第6PLNQ:声明式数据并行 6.1从LINQ转换到PLINQ 6.1.1 ParallelEnumerable及其AsParallel方法 6.1.2 AsOrdered和orderby子句 6.2指定执行模式 6.3理解PLINQ中的数据分区 6.4通过PLINQ执行归约操作 6.5创建自定义的PLINQ聚合函数 6.6并发PLINQ任务 6.7取消PLINQ 6.8指定所需的并行度 6.8.1 WithDegreeOfParallelism 6.8.2测量可扩展性 6.9使用ForAll 6.9.1 foreach和ForAll的区别 6.9.2测量可扩展性 6.10通过WithMergeOptions配置返回结果的方式 6.11处理PLINQ抛出的异常 6.12使用PLINQ执行MapReduce 算法 6.13使用PLINQ设计串行多步操作 6.14小结 第7Visual Studio 2010的任务调试能力 7.1充分利用多显示器的支持 7.2理解并行任务调试器窗口 7.3查看Parallel Stacks图 7.4跟踪并发代码 7.4.1调试匿名方法 7.4.2查看方法 7.4.3在源代码中查看线程 7.5检测死锁 7.6小结 第8 线程池 8.1探究任务的底层技术 8.2理解新的CLR 4线程池引擎 8.2.1理解全局队列 8.2.2等待工作线程完成工作 8.2.3跟踪动态数目的工作线程 8.2.4使用任务(而不是线程)将作业加入队列 8.2.5理解任务和线程池之间的关系 8.2.6理解局部队列和工作窃取算法 8.2.7指定自定义的任务调度器 8.3小结 第9 异步编程模型 9.1结合使用异步编程和任务 9.1.1使用TaskFactory.FromAsync 9.1.2编写异步方法执行结束之后的延续 9.1.3合并多个并发异步操作的结果 9.1.4执行异步WPF UI更新 9.1.5执行异步Windows Forms UI更新 9.1.6创建执行EAP操作的任务 9.1.7使用TaskCompletionSource 9.2小结 第10 并行测试和调优 10.1准备并行测试 10.1.1使用性能剖析功能 10.1.2测量并发性 10.2常见问题模式的解决方案 10.2.1串行化的执行 10.2.2锁争用 10.2.3锁封护 10.2.4申请超额 10.2.5申请不足 10.2.6分区问题 10.2.7工作站垃圾回收开销 10.2.8使用服务器垃圾回收 10.2.9 I/O瓶颈 10.2.10主线程过载 10.3理解伪共享 10.4小结 第11 向量化、SIMD指令以及其他并行库 11.1理解SIMD和向量化 11.2从MMX到SSE4.X和AVX 11.3使用Intel Math KernelLibrary 11.3.1使用适用于多核的高度优化的软件函数 11.3.2将基于任务的编程和外部优化的库混合使用 11.3.3并行生成伪随机数 11.4使用Intel IntegratedPerformance Primitives 11.5小结 附录A.NET4中与并行相关的类图 附录B并发UML模型 附录C Parallel Extensions Extras
原作名: Professional Parallel Programming with C# 内容简介 · · · · · · 您想淋漓尽致地发挥多核计算机系统的处理能力吗?《C#并行编程高级教程:精通NET 4 Parallel Extensions》将帮助您实现这一夙愿。这本精品书籍浓墨重彩地描述如何使用C# 4、Visual Studio 2010和.NET Framework 4高效地创建基于任务的并行应用程序,详细讲述最新的单指令、多数据流指令和向量化等并行编程技术,介绍现代并行库,讨论如何珠联璧合地使用高级Intel工具与C#,并指导您巧妙使用新引入的轻型协调结构来开发自己的解决方案并解决最棘手的并发编程问题。 主要内容 ◆介绍如何基于新Task Parallel Library和.NET 4设计稳定的可扩展并行应用程序。 ◆讲解命令式数据并行、命令式任务并行并发集合以及协调数据结构。 ◆描述PLINQ高级声明式数据并行。 ◆讨论如何使用新的Visual Studio 2010并行调试功能来调试匿名方法、任务和线程。 ◆演示如何对数据源进行分区,以便在不同任务和线程之间合理分配工作负荷。 作者简介 · · · · · · 从8岁起就开始使用计算机了。在20世纪80年代初,他开始在传奇的TexasTI-99/4A和Commodore64家用计算机上编写程序。他作为一名优秀毕业生在UADE大学获得了学士学位,然后又在UCEMA大学凭借出色的毕业论文获得了工商管理硕士学位。 自1997年以来,Gaston在并行编程、多处理器和多核处理器领域进行了深入研究。在设计和开发各种类型复杂的利用多核处理能力的并行解决方案方面,他有着14年的丰富经验,后来,他开始通过C#和.NETFramework编写并行解决方案。
©️2020 CSDN 皮肤主题: 编程工作室 设计师:CSDN官方博客 返回首页