博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
《C#并行编程高级教程》第5章 协调数据结构 笔记
阅读量:5146 次
发布时间:2019-06-13

本文共 3193 字,大约阅读时间需要 10 分钟。

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

System.Threading.Barrier

用于一段程序分成多个阶段,每个阶段的开始都需要之前的阶段完成。如果这段程序需要并行化。可以在每段之间采用Barrier。
还可以设置在每个阶段之间的动作。
task在Barrier中成为参与者(participant),在构造的时候要设定数量,也可以动态的增删。
异常和超时的处理可以参考代码。
相比于使用使用Task的ContinueWith方法实现多个阶段的串行,Barrier可以减少非常多的Task数量。
用完后需要Dispose
Task[] _tasks;
Barrier _barrier;
 
_tasks
=
new Task[
4];
_barrier
=
new Barrier(
4, (barrier)
=
>
{
    Console.WriteLine(
"Current phase: {0}",
        barrier.CurrentPhaseNumber);
});
 
for (
int i
=
0; i
<
4; 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来设置超时
bool lockTaken
=
false;
try
{
    Monitor.TryEnter(_obj,
2000,
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
=
new SpinLock(
false);
bool lockTaken
=
false;
try
{
    sl.TryEnter(
2000,
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,为多线程的共享变量提供原子操作。
包括 递增 递减 加法 赋值 比较 读 等等。
int total
=
0;
Interlocked.Increment(
ref total);
其中需要注意的一点,如果在32位系统下。对64位数值的读取不是原子操作。需要使用Interlocked.Read(ref long location)。64位系统不需要,直接访问就可以了。

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

你可能感兴趣的文章
Xcode5和ObjC新特性
查看>>
LibSVM for Python 使用
查看>>
Centos 7.0 安装Mono 3.4 和 Jexus 5.6
查看>>
CSS属性值currentColor
查看>>
java可重入锁reentrantlock
查看>>
浅谈卷积神经网络及matlab实现
查看>>
解决ajax请求cors跨域问题
查看>>
《收获,不止Oracle》pdf
查看>>
Real-Time Rendering 笔记
查看>>
如何理解HTML结构的语义化
查看>>
Activity之间的跳转:
查看>>
实验四2
查看>>
多路复用
查看>>
Python数据可视化之Pygal(雷达图)
查看>>
Java学习笔记--字符串和文件IO
查看>>
转 Silverlight开发历程—(画刷与着色之线性渐变画刷)
查看>>
SQL语法(3)
查看>>
在js在添版本号
查看>>
sublime3
查看>>
Exception Type: IntegrityError 数据完整性错误
查看>>