同步

  当多个线程共享一些数据的时候,我们就需要使用同步技术,确保一次只有一个线程访问合改变共享状态。注意,同步问题与争用和死锁有关。

例:

static int idx = ;
static void Add()
{
for (int i = ; i < ; i++)
{
idx++;
}
}
static void Main()
{
const int SIZE = ;
Task[] arr = new Task[SIZE];
while (true)
{
for (int i = ; i < SIZE; i++)
{
arr[i] = new Task(Add);
arr[i].Start(); //启动多个线程
} for (int i = ; i < SIZE; i++)
{
arr[i].Wait(); //等待线程完成
} Console.WriteLine(idx);
Thread.Sleep();
idx = ;// 重置数据,再次运行
}
}

结果:

1717634
1652989
1444839
1272385
1558097
1297459
1968232
2000000

显然,不是我们想要的,我们期望每次运行的结果都是2000000。这是因为idx++不是线程安全的,它的操作包括从内存中获取一个值,给该值递增1,再将它存回内存。这些操作都可能会被线程调度器打断。

这种情况下,我们就需要一些同步方法解决该问题。

  • lock关键字将语句块标记为临界区,方法是获取给定对象的互斥锁,执行语句,然后释放该锁。在块的开始处调用 Enter,而在块的结尾处调用 Exit。这样可确保当一个线程位于代码的关键部分时,另一个线程不会进入该关键部分。 如果其他线程尝试进入锁定的代码,则它将一直等待(即被阻止),直到该对象被释放。一个线程,当阻塞的时候,不占用CPU资源。
static object locker = new object();
static void Add()
{
for (int i = ; i < ; i++)
{
lock (locker)
idx++;
}
}
  • Interlocked类用于使变量的简单语句原子话(最小执行单元,不会被中途打断),提供了以线程安全的方式递增、递减、交换和读取值的方法。
对上例而言,把idx++替换成Interlocked.Increment(ref idx);
  • Monitor类算是实现锁机制的纯正类,lock语句由编译器解析为使用Monitor类。
lock(obj)
{
//synchronized region for obj
} 相当于 Monitor.Enter(obj);
try
{
//synchornized region for obj
}
finally
{
Monitor.Exit(obj)
}

用TryEnter可以添加timeout

 object obj = new object();
Task.Run(()=>{
lock(obj)
{
Console.WriteLine("lock obj");
Thread.Sleep();
}
});
bool b = Monitor.TryEnter(obj, );
if (b)
{
try
{
Console.WriteLine("monitor enter.");
}
finally
{
Monitor.Exit(obj);
}
}
else
{
Console.WriteLine("monitor enter false.");
} Console.ReadKey();

另外,Monitor还提供了Wait方法,用于释放对象上的锁并阻止当前线程,直到它重新获取该锁。

提供了Pulse方法用于通知等待队列中的线程锁定对象状态的更改;PulseAll通知所有的等待线程对象状态的更改。

  •  SpinLock自旋锁,如果基于对象锁定(Monitor)的系统开销由于垃圾回收而过高,就可以使用SpinLock结构。如果有大量的锁定(例如,列表中的每个节点都有一个锁定),且锁定的时间总是非常短,SpinLock结构就很有用。应避免使用多个SpinLock结构,也不要调用任何可能阻塞的内容。SpinLock 应仅用于您,这样做可以改进应用程序的性能确定后。 还有一点需要注意 SpinLock 是值类型,为了提高性能。 出于此原因,您必须非常小心,以免意外复制 SpinLock 实例,因为两个实例 (原始项和副本) 都将完全相互独立的这可能会导致错误行为的应用程序。 如果 SpinLock 必须围绕传递实例,则应通过引用而不是通过值传递。

    请不要在存储 SpinLock 只读字段中的实例。

C# 多线程系列(六)的更多相关文章

  1. java多线程系列(六)---线程池原理及其使用

    线程池 前言:如有不正确的地方,还望指正. 目录 认识cpu.核心与线程 java多线程系列(一)之java多线程技能 java多线程系列(二)之对象变量的并发访问 java多线程系列(三)之等待通知 ...

  2. java多线程系列六、线程池

    一. 线程池简介 1. 线程池的概念: 线程池就是首先创建一些线程,它们的集合称为线程池. 2. 使用线程池的好处 a) 降低资源的消耗.使用线程池不用频繁的创建线程和销毁线程 b) 提高响应速度,任 ...

  3. Java多线程系列六——Map实现类

    参考资料: https://crunchify.com/hashmap-vs-concurrenthashmap-vs-synchronizedmap-how-a-hashmap-can-be-syn ...

  4. 【Java多线程系列六】Map实现类

    Map的一些实现类有及其特性 类 线程安全 特性 Hashtable 是 Key不能为null HashMap 否 读写效率最高,但在Java6多线程环境下使用不当可能陷入死循环,进而导致CPU使用率 ...

  5. (Java多线程系列六)join()的用法和线程的优先级

    join()的用法和线程的优先级 1.join()的用法 join()作用就是让其他线程处于等待状态 先看一个需求:创建一个线程,子线程执行完毕后,主线程才能执行 public class JoinT ...

  6. java多线程系列(一)

    java多线程技能 前言:本系列将从零开始讲解java多线程相关的技术,内容参考于<java多线程核心技术>与<java并发编程实战>等相关资料,希望站在巨人的肩膀上,再通过我 ...

  7. java多线程系列(二)

    对象变量的并发访问 前言:本系列将从零开始讲解java多线程相关的技术,内容参考于<java多线程核心技术>与<java并发编程实战>等相关资料,希望站在巨人的肩膀上,再通过我 ...

  8. java多线程系列(三)---等待通知机制

    等待通知机制 前言:本系列将从零开始讲解java多线程相关的技术,内容参考于<java多线程核心技术>与<java并发编程实战>等相关资料,希望站在巨人的肩膀上,再通过我的理解 ...

  9. java多线程系列(四)---Lock的使用

    Lock的使用 前言:本系列将从零开始讲解java多线程相关的技术,内容参考于<java多线程核心技术>与<java并发编程实战>等相关资料,希望站在巨人的肩膀上,再通过我的理 ...

  10. java多线程系列(五)---synchronized ReentrantLock volatile Atomic 原理分析

    java多线程系列(五)---synchronized ReentrantLock volatile Atomic 原理分析 前言:如有不正确的地方,还望指正. 目录 认识cpu.核心与线程 java ...

随机推荐

  1. mysql 查询出的数组为null怎么转换成0

    mysql 查询出的数组为null怎么转换成0 IFNULL(b.dayPay,0) as yesterdayPay,

  2. 2018.5.7每天一题面试题----final, finally, finalize 的区别

    1.final修饰符(关键字).被final修饰的类,就意味着不能再派生出新的子类,不能作为父类而被子类继承. 因此一个类不能既被abstract声明,又被final声明.将变量或方法声明为final ...

  3. 51. spring boot属性文件之多环境配置【从零开始学Spring Boot】

    原本这个章节是要介绍<log4j多环境不同日志级别的控制的>但是没有这篇文章做基础的话,学习起来还是有点难度的,所以我们先一起了解下spring boot属性文件之多环境配置,当然文章中也 ...

  4. [luoguP2486] [SDOI2011]染色(树链剖分)

    传送门 就是个模板啦 记录每一个点的左端点颜色和右端点颜色和当前端点颜色段数. 合并时如果左孩子右端点和右孩子左端点不同就 ans-- 在重链上跳的时候别忘记统计一下 ——代码 #include &l ...

  5. Minimum Sum LCM(uva 10791)

    题意(就是因为读错题意而wa了一次):给一个数字n,范围在[1,2^23-1],这个n是一系列数字的最小公倍数,这一系列数字的个数至少为2 例如12,是1和12的最小公倍数,是3和4的最小公倍数,是1 ...

  6. XAPIAN简单介绍(三)

    今天主要介绍的是Xapian::Database这个类.先上图 看上去就非常恐怖的吧,我们一点点的说. 首先一切的開始都来自那个include目录中的database.h,他的直接实现是在omdata ...

  7. Domino V8 在 UNIX/Linux 平台上的安装及其常见问题

    在 IBM Bluemix 云平台上开发并部署您的下一个应用. 开始您的试用 Domino V8 的安装需求 Domino V8 可以支持多种平台和操作系统,表1 列出了其支持的各种 UNIX/Lin ...

  8. 利用scons构建project

    scons有非常多相对于make构建系统的优秀特性,可是因为发展时间比較短如今的应用范围还是不太多,可以找到的资料也不是非常多. scons如今一大问题就是初始上手还是有点难度的,对于有python的 ...

  9. HDU 2461 线段树扫描线

    给出N个矩形,M次询问 每次询问给出R个.问这R个矩形围成的面积 经典扫面线求面积并,对每次询问的R个点离散化一下 #include "stdio.h" #include &quo ...

  10. iOS_截屏并裁剪

    截图使用场景: watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQvcHJlX2VtaW5lbnQ=/font/5a6L5L2T/fontsize/400/fil ...