本章介绍了一些轻量级的同步原语,其中有很大部分是.NET Framework 4才引入的。

System.Threading.Barrier

用于一段程序分成多个阶段,每个阶段的开始都需要之前的阶段完成。如果这段程序需要并行化。可以在每段之间采用Barrier。

还可以设置在每个阶段之间的动作。
task在Barrier中成为参与者(participant),在构造的时候要设定数量,也可以动态的增删。
异常和超时的处理可以参考代码。
相比于使用使用Task的ContinueWith方法实现多个阶段的串行,Barrier可以减少非常多的Task数量。
用完后需要Dispose
Task[] _tasks;
Barrier _barrier;
 
_tasks ];
_barrier , (barrier) ; i ; i++)
{
    _tasks[i] = Task.Factory.StartNew((num) =>
    {
        //...阶段1
        if (!_barrier.SignalAndWait(TIMEOUT))
        {
            //...
        }
        //...阶段2
        try
        {
            _barrier.SignalAndWait();
        }
        catch (BarrierPostPhaseException bppex)
        {
            //..
            break;
        }
        //...阶段3
        _barrier.SignalAndWait();
    }, i);
}

互斥锁

C#提供了lock关键字来获取一个互斥锁。lock块编译时会被替换成System.Threading.Monitor的使用。
需要注意的点有
  • lock和Monitor只能锁引用类型的实例,不要对值类型使用lock或Monitor。
  • 要避免锁定我iabuduixinag,避免跨成员或类的边界获得或释放锁。
  • 临界区中的代码应该尽量保持简单。
lock (_obj)
{
    //...
}
//编译时lock块会被替换成如下
bool lockTaken = false;
Monitor.Enter(_obj, ref lockTaken);
try
{
    //...
}
finally
{
    if (lockTaken)
    {
        Monitor.Exit(_obj);
    }
}
如果是直接使用Monitor还可以使用TryEnter来设置超时
, ref lockTaken);
    if (!lockTaken)
    {
        throw new TimeoutException(...);
    }
    //...
}
finally
{
    if (lockTaken)
    {
        Monitor.Exit(_obj);
    }
}

自旋

Monitor开销非常大。如果锁的时间非常短,自旋锁能获取更好的性能。
但是如果长时间的自旋,SpinWait会让出时间片,并触发上下文切换。这和忙等不同。
如果多个任务都需要自旋锁,那么每一个任务都应该使用自己的实例
SpinWait在单核没有实际意义,因为必然是要做上下文切换才有可能等到的。
SpinWait.SpinUntil(Func<bool> condition,int millisecondsTimeout)提供了基于自旋的等待发方案
var sl , ref lockTaken);
    if (!lockTaken)
    {
        throw new TimeoutException(...);
    }
    //....
}
finally
{
    if (lockTaken)
    {
        sl.Exit(false);
    }
}
 

System.Threading.ManualResetEventSlim

ManualResetEventSlim是ManualResetEvent的简化版。不能跨进程或跨AppDomain。
ManualResetEventSlim是一个带有两个可能状态的事件对象,设置信号(true)和取消信号(false)
利用这个可以进行通讯
使用完后需要Dispose。
private ManualResetEventSlim manualResetEvent1;
private ManualResetEventSlim manualResetEvent2;
//method1
try
{
    manualResetEvent1.Set();
    //..
}
finally
{
    manualResetEvent1.Reset();
}
//method2
try
{
    manualResetEvent2.Set();
    if (!manualResetEvent1.Wait(TIMEOUT))
    {
        throw new TimeoutException(...);
    }
    //...
}
finally
{
    // Switch to unsignaled/unset
    manualResetEvent2.Reset();
}

System.Threading.SemaphoreSlim

System.Threading.Semaphore的轻量级版本。不能跨进程或跨AppDomain。
提供一个信号量机制来限制资源的并发访问。
用完之后需要Dispose。
SemaphoreSlim _semaphore;
 
_semaphore.Wait();
try
{
    //...
}
finally
{
    _semaphore.Release();
}
 

System.Threading.CountdownEvent

CountdownEvent带有一个初始计数器,可以发出一个信号,令计数减一。调用Wait方法时会被阻塞直到计数器达到0。
用完后需要Dispose
private static CountdownEvent _countdown;
 
//Main thread
_countdown = new CountdownEvent(MIN_PATHS);
//...
try
{
    //new Task
    //...
}
finally
{
    _countdown.Dispose();
}
 
 
//Task1
try
{
    //...
}
finally
{
    _countdown.Signal();
}
 
//Task2
_countdown.Wait();
//...
 

原子操作

在并行的环境下对共享变量的一些最基本的操作都是不安全的。
把共享变量的操作都加锁,代价又太大。
System.Threading.Interlocked,为多线程的共享变量提供原子操作。
包括 递增 递减 加法 赋值 比较 读 等等。
;
Interlocked.Increment(ref total);

其中需要注意的一点,如果在32位系统下。对64位数值的读取不是原子操作。需要使用Interlocked.Read(ref long location)。64位系统不需要,直接访问就可以了。

《C#并行编程高级教程》第5章 协调数据结构 笔记的更多相关文章

  1. 【读书笔记】.Net并行编程高级教程(二)-- 任务并行

    前面一篇提到例子都是数据并行,但这并不是并行化的唯一形式,在.Net4之前,必须要创建多个线程或者线程池来利用多核技术.现在只需要使用新的Task实例就可以通过更简单的代码解决命令式任务并行问题. 1 ...

  2. 【读书笔记】.Net并行编程高级教程--Parallel

    一直觉得自己对并发了解不够深入,特别是看了<代码整洁之道>觉得自己有必要好好学学并发编程,因为性能也是衡量代码整洁的一大标准.而且在<失控>这本书中也多次提到并发,不管是计算机 ...

  3. Net并行编程高级教程--Parallel

    Net并行编程高级教程--Parallel 一直觉得自己对并发了解不够深入,特别是看了<代码整洁之道>觉得自己有必要好好学学并发编程,因为性能也是衡量代码整洁的一大标准.而且在<失控 ...

  4. 《C#并行编程高级教程》第9章 异步编程模型 笔记

    这个章节我个人感觉意义不大,使用现有的APM(异步编程模型)和EAP(基于时间的异步模型)就很够用了,针对WPF和WinForm其实还有一些专门用于UI更新的类. 但是出于完整性,还是将一下怎么使用. ...

  5. 《C#并行编程高级教程》第6章 PLINQ:声明式数据并行 笔记

    PLINQ这个话题好多书都写到过,这本也没有什么特别好的地方. 几个有用和有趣的点记录一下.   顺序的不确定性 用PLINQ就一定要记住并行后会导致顺序不确定的问题.解决方案就是AsOrdered或 ...

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

    这一章主要介绍了System.Collections.Concurrent下的几个类. ConcurrentQueue<T> 并发队列.完全无锁,使用CAS(compare-and-swa ...

  7. 《C#并行编程高级教程》第2章 命令式编程 笔记

    Parallel.Invoke 并行执行多个方法,只有在所有方法都执行后才会返回 static void Main(string[] args){    Parallel.Invoke(    () ...

  8. 《C#并行编程高级教程》第8章 线程池 笔记

    主要的几个概念(详细最好还是看书,配合插图看)   任务是会被分配到线程上的,而这些线程都在线程池引擎下管理 线程池引擎管理着合适数量的线程池,线程从全局队列获取工作项执行. .NET4 Framew ...

  9. 《C#并行编程高级教程》第7章 VS2010任务调试 笔记

    没有什么好说的,主要是将调试模式下的Parallel Tasks窗体和Parallel Stacks窗体.折腾一下应该比看书效果好.(表示自己没有折腾过) 另外值得注意的是,主线程不是一个任务.所以主 ...

随机推荐

  1. C++ GUI Programming with Qt4 笔记 -- chap2 QDialog

    以 finddialog 为例,介绍 QDialog.并对 Qt 的基本概念和技巧做了更进一步(chap1)的介绍. 1.MOC 扩展与signals–slots 机制 严格来说,Qt 开发,写的并不 ...

  2. iOS 构造方法

    构造方法:用于在对象创建出来的时候为对象的成员变量或者属性赋值 1.构造方法就是初始化对象的方法,它属于对象方法.构造方法分为系统自带和自定义构造方法. 如何创建自定义构造方法: 属于对象方法那么以  ...

  3. Sql语句批量更新数据(多表关联)

    最近在项目中遇到一个问题,原来设计的功能是不需要一个特定的字段值depid的,但是新的功能需要根据depid来展现,于是出现了这样一个问题,新增加的数据都有正确的depid,而原来的大量的数据就没有d ...

  4. MySQL中SQL语句的分类

    1:数据定义语言(DDL) :创建和删除数据库(CREATE DATABASE || DROP  DATABASE):2:创建.修改.重命名.删除表(CREATE  TABLE || ALTER TA ...

  5. ECshop网店系统百万级商品量性能优化-简单的一些Cache内存配置

    ECshop网店系统对于产品的数据.模板.Query都可以缓存,也就是把一些商品详情页.分类页.Search页的数据经过一次访问后,用文件的形式保存下来,下次有人访问相同的页面时,不用再查数据库,直接 ...

  6. hdu 5510 Bazinga KMP+尺取法

    题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=5510 题意:至多50组数据,每组数据至多500个字符串,每个字符串的长度最长为2000.问最大的下标( ...

  7. 数组有N+M个数字, 数字的范围为1 ... N, 打印重复的元素, 要求O(M + N), 不可以用额外的空间

    数组有N+M个数字, 数字的范围为1 ... N, 打印重复的元素, 要求O(M + N), 不可以用额外的空间 1.题目中要求我们不能使用额外的空间,那么我们能采用在原数组上做文章,这里的重点是如何 ...

  8. 使用.net 的Chart控件绘制曲线图

    在进行软件开发过程中我们可能会碰到需要生成图表的情况,在.NET中以前经常用GDI去绘制,虽然效果也不错,自从.NET 4.0开始,专门为绘制图表而生的Chart控件出现了,有了它,就可以轻松的绘制你 ...

  9. vs运行代码版本不一致删除缓存

    C:\Windows\Microsoft.NET\Framework64\v4.0.30319\Temporary ASP.NET Files

  10. 《深入理解javascript原型和闭包系列》 知识点整理

    深入理解javascript原型和闭包系列 对原型和闭包等相关知识的讲解,由浅入深,通俗易懂,每个字都值得细细研究. 一.一切都是对象 1. typeof操作符输出6种类型:string boolea ...