本章介绍了一些轻量级的同步原语,其中有很大部分是.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. mysql innodb 数据打捞(四)innodb 簇不连续页扫描提取(试验)

    一,用winhex把正常页有意做成不连续的两部分,把后8K向后移动4K,中间隔开4K,启动第一次扫描; 扫描结果是,没有提取到有效页面,但在输出目录生成两个文件:upper.pages和upper.l ...

  2. rar压缩文件下载

    //string fileName = "ceshi.rar";//客户端保存的文件名         //string filePath = Server.MapPath(&qu ...

  3. mysql 查看数据库大小

    select table_schema, concat(truncate(sum(data_length)/1024/1024,2),' mb') as data_size,concat(trunca ...

  4. firemonkey 得到屏幕信息

    type TO_MONITOR = class hm: HMONITOR; end; function EnumMonitorsProc(hm: HMONITOR; dc: HDC; r: PRect ...

  5. Python 强大而易用的文件操作(转载)

    在Python中可以很方便地做一些诸如浏览目录,创建文件夹,删除文件夹等等的操作. 对文件系统的访问大多通过os模块来实现,因为Python是多平台的,os模块只是前端,具体的实现还是由具体的系统来完 ...

  6. 洛谷 P1417 烹调方案

    题目背景 由于你的帮助,火星只遭受了最小的损失.但gw懒得重建家园了,就造了一艘飞船飞向遥远的earth星.不过飞船飞到一半,gw发现了一个很严重的问题:肚子饿了~ gw还是会做饭的,于是拿出了储藏的 ...

  7. GCC 警告提示的用法

    转自GCC 警告提示的用法 本节主要讲解GCC的警告提示功能.GCC包含完整的出错检查和警告提示功能,它们可以帮助Linux程序员写出更加专业和优美的代码.我们千万不能小瞧这些警告信息,在很多情况下, ...

  8. Nginx完整配置说明

    http://blog.csdn.net/marising/article/details/3979493 可以参考如下的完整例子 http://wiki.codemongers.com/NginxF ...

  9. maven 依赖排除

    在项目中遇到一个问题,项目使用spring 3.x,引用了某些包,这些包又依赖了spring2.x,造成项目无法启动.这种情况就需要用到maven的依赖排除,配置如下: 使用以下代码排除依赖xxxx引 ...

  10. 【Uva11212】 Editing a Book(IDA*)

    [题意] 有n个数字的全排列,每次可以剪切一段粘贴到某个位置.问最后变成升序最少多少步. 如"{2,4,1,5,3,6}要2步 {3,4,5,1,2}只要一步 [分析] 迭代深搜真的AC了也 ...