本章介绍了一些轻量级的同步原语,其中有很大部分是.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. 为UITextView添加与UITextField一样的边框——UITextField默认边框颜色、宽度、圆角

    我的技术博客经常被流氓网站恶意爬取转载.请移步原文:http://www.cnblogs.com/hamhog/p/3789052.html,享受整齐的排版.有效的链接.正确的代码缩进.更好的阅读体验 ...

  2. 盗链网易163、腾讯QQ、新浪sina、百度Baidu的图片之PHP独立版

    网易相册频道,网易相册确实是一个高质量图片的地方,而且免费,唯一缺点是很多加了水印,但这个不重要了,无意间发现网易163相册频道的图片是防止盗链的,于是便自己写了一个小程序来突破这个. 本盗链图片最新 ...

  3. Python 中模块间全局变量的使用上的注意

    最近用Python写代码,需要用到模块间的全局变量. 网上四处搜索,发现普遍做法是把全局变量放到一个独立的模块中,使用时,导入此全局变量模块即可. 但是在实际使用过程中发现了些小问题:在使用如下代码导 ...

  4. Binding的源和路径

    书上写着:Binding的源也就是数据的源头.Binding对于源的要求很简单-只要他是一个对象!并且通过属性(Property)公开自己的数据,它就可以作为Binding的源了.就像上一篇我写的那个 ...

  5. Spring核心框架 - AOP之动态代理机制

    动态代理类的字节码在程序运行时由Java反射机制动态生成,无需程序员手工编写它的源代码.动态代理类不仅简化了编程工作,而且提高了软件系统的可扩展性,因为Java 反射机制可以生成任意类型的动态代理类. ...

  6. oracle数据库的建表,删除字段,添加字段,修改字段,修改字段......

    1. 使用oracle创建一张表: SQL> create table loginuser( id ,), username ), password ), email ), descriable ...

  7. 九张图让你的PPT立刻高大上

  8. codeforces 392A Blocked Points

    我的方式是用暴力的方法找到每一行每一列的边界点: 但是有大神直接用一个公式解决了:floor(n*sqrt(2))*4: 想了很久还是不理解,求各路大神指点! #include<iostream ...

  9. 中国海洋大学第四届朗讯杯高级组 A Rocky

    http://acm.sdut.edu.cn/sdutoj/showproblem.php?pid=2718&cid=1203 题意:给你一个m乘n的格子阵,从一边进去,直线往前走,如果前边有 ...

  10. SQL跨表更新

    [一篮饭特稀原创,转载请注明出自http://www.cnblogs.com/wanghafan/p/4384039.html]  前提:两张表要更新的字段.关联字段结构一致 更新库:FJPDI_TZ ...