如果程序用到了并发技术,那就要特别留意这种情况:一段代码需要修改数据,同时其他代码需要访问同一个数据。

这种情况就需要考虑同步地访问数据。

如果下面三个条件都满足,就必须用同步来保护共享的数据。

  • 多段代码正在并发运行;

  • 这几段代码在访问(读或写)同一个数据;

  • 至少有一段代码在修改(写)数据。

一 阻塞锁

如果有多个线程需要安全地读写共享数据,这种情况可以考虑使用lock语句。

一个线程进入锁后,在锁被释放之前其他线程是无法进入的:

class MyClass
{
private readonly object _mutex = new object();
private int _value;
public void Increment()
{
lock (_mutex)
{
_value = _value + 1;
}
}
}

.NET 框 架 中 还 有 很 多 其 他 类 型 的 锁, 如 Monitor、SpinLock、ReaderWriterLockSlim。

关于lock的使用,有四条重要的准则:

  • 限制锁的作用范围:

    要尽量限制锁的作用范围。应该把 lock 语句使用的对象设为私有成员,并且永远不要暴露给非本类的方法。

    每个类型通常最多只有一个锁。如果一个类型有多个锁,可考虑通过重构把它分拆成多个独立的类型。

    lock可以锁定任何引用类型,但是建议为 lock 语句定义一个专用的成员,就像上例中那样。

  • 文档中明确锁保护的内容;

  • 锁范围内的代码尽量少:在锁定时执行的代码要尽可能得少。要特别小心阻塞调用。在锁定时不要做任何阻塞操作。

  • 在控制锁的时候绝不运行随意的代码:

    在锁定时绝不要调用随意的代码。随意的代码包括引发事件、调用虚拟方法、调用委托。

    如果一定要运行随意的代码,就在释放锁之后运行。

二 异步锁

如果多个代码块需要安全地读写共享数据,并且这些代码块可能使用 await 语句,可以考虑使用SemaphoreSlim。

class MyClass1
{
private readonly SemaphoreSlim _mutex = new SemaphoreSlim(1); private int _value; public async Task DelayAndIncrementAsync()
{
await _mutex.WaitAsync();
try
{
var oldValue = _value;
await Task.Delay(TimeSpan.FromSeconds(oldValue));
_value = oldValue + 1;
}
finally
{
_mutex.Release();
}
}
}

三 阻塞信号

如果需要从一个线程发送信号给另一个线程,可以考虑使用ManualResetEventSlim。

一个ManualResetEventSlim的对象处于这两种状态其中之一:标记的(signaled)或未标记的(unsignaled)。

每个线程都可以把事件设置为signaled 状态,也可以把它重置为 unsignaled 状态。线程也可等待事件变为 signaled 状态。

下面的两个方法被两个独立的线程调用,一个线程等待另一个线程的信号:

class MyClass2
{
private readonly ManualResetEventSlim _mres = new ManualResetEventSlim();
private int _value;
public int WaitForInitialization()
{
_mres.Wait();
return _value;
}
public void InitializeFromAnotherThread()
{
_value = 13;
_mres.Set();
}
}

在 .NET 框架中,还有一些线程同步信号类型。ManualResetEvent、AutoResetEvent、CountdownEvent。

四 异步信号

需要在代码的各个部分间发送通知,并且要求接收方必须进行异步等待。

如果该通知只需要发送一次,那可用 TaskCompletionSource<T> 异步发送。

发送代码调用TrySetResult,接收代码等待它的 Task 属性:

class MyClass3
{
private readonly TaskCompletionSource<object> _tcs = new TaskCompletionSource<object>();
private int _value1;
private int _value2;
public async Task<int> WaitForInitializationAsync()
{
await _tcs.Task;
return _value1 + _value2;
}
public void Initialize()
{
_value1 = 13;
_value2 = 17;
_tcs.TrySetResult(null);
}
}

以上。

C#并发编程-4 同步的更多相关文章

  1. Java并发编程:同步容器

    Java并发编程:同步容器 为了方便编写出线程安全的程序,Java里面提供了一些线程安全类和并发工具,比如:同步容器.并发容器.阻塞队列.Synchronizer(比如CountDownLatch). ...

  2. 【转】Java并发编程:同步容器

    为了方便编写出线程安全的程序,Java里面提供了一些线程安全类和并发工具,比如:同步容器.并发容器.阻塞队列.Synchronizer(比如CountDownLatch).今天我们就来讨论下同步容器. ...

  3. 8、Java并发编程:同步容器

    Java并发编程:同步容器 为了方便编写出线程安全的程序,Java里面提供了一些线程安全类和并发工具,比如:同步容器.并发容器.阻塞队列.Synchronizer(比如CountDownLatch). ...

  4. Python并发编程-线程同步(线程安全)

    Python并发编程-线程同步(线程安全) 作者:尹正杰 版权声明:原创作品,谢绝转载!否则将追究法律责任. 线程同步,线程间协调,通过某种技术,让一个线程访问某些数据时,其它线程不能访问这些数据,直 ...

  5. Java并发编程之同步

    1.synchronized 关键字 synchronized 锁什么?锁对象. 可能锁对象包括: this, 临界资源对象,Class 类对象. 1.1 同步方法 synchronized T me ...

  6. Python并发编程之同步\异步and阻塞\非阻塞

    一.什么是进程 进程: 正在进行的一个过程或者说一个任务.而负责执行任务则是cpu. 进程和程序的区别: 程序仅仅只是一堆代码而已,而进程指的是程序的运行过程. 需要强调的是:同一个程序执行两次,那也 ...

  7. Java并发编程:同步锁、读写锁

    之前我们说过线程安全问题可以用锁机制来解决,即线程必要要先获得锁,之后才能进行其他操作.其实在 Java 的 API 中有这样一些锁类可以提供给我们使用,与其他对象作为锁相比,它们具有更强大的功能. ...

  8. Java并发编程之同步/并发集合

    同步集合 Java中同步集合如下: Vector:基于数组的线程安全集合,扩容默认增加1倍(ArrayList50%) Stack:继承于Vector,基于动态数组实现的一个线程安全的栈 Hashta ...

  9. Java并发编程基础——同步

    一.synchronized 关键字 1)synchronized 锁什么?锁对象.可能锁对象包括: this, 临界资源对象,Class 类对象.如同下面例子所示: package cn.test. ...

随机推荐

  1. DelayQueue达到定时触发效果

    DelayQueue的特点就是插入Queue中的数据可以按照自定义的delay时间进行排序.只有delay时间小于0的元素才能够被取出. 这样子,只要开启一个线程循环从DelayQueue中取值执行, ...

  2. React + Antd Menu组件实现菜单树

    准备好两个变量,一个用来保存平级菜单列表,一个用来保存遍历后的菜单树. 推荐后端返回平级菜单树,假如菜单比较多,可以直接结合find方法找到菜单,做搜索功能很省事. const [menuList, ...

  3. list集合的介绍和常用方法

    List接口介绍 java.util.List接口继承自Collection接口,是单列集合的一个重要分支,习惯性地会将实现了List接口的对象成为List集合.在List集合中允许出现重复的元素,所 ...

  4. 20220723-Mac上使用IntelliJ IDEA

    目录 IDEA快捷键 IDEA模板 常用模板快捷键 个人随笔 软件:IntelliJ IDEA 电脑:Mac IDEA快捷键 打开/关闭 项目视图 快捷键:⌘ + 1 运行项目 快捷键:⌃ + ⇧ + ...

  5. 当我们谈论算法我们在谈论什么:由疫情核酸检测想到的分治算法(Divide-and-Conquer)

    原文转载自「刘悦的技术博客」https://v3u.cn/a_id_159 北京的疫情一波未平一波又起,由此看来,战"疫"将是一场旷日持久的战争,绝不能掉以轻心.轻易言胜.病毒随时 ...

  6. 下载markdown软件Obsidian(解决官网下载速度慢)

    Typora要钱了,不想每次都点稍后再买. Obsidian也很好用,官网是:https://obsidian.md/ 但是不太好下载,直接下载速度只有10kb/s左右,总共60多MB: 扔给迅雷也没 ...

  7. 我与Apache DolphinScheduler的成长之路

    关于 Apache DolphinScheduler社区 Apache DolphinScheduler(incubator) 于17年在易观数科立项,19年3月开源, 19 年8月进入Apache ...

  8. 「CCO 2017」专业网络

    Kevin 正在一个社区中开发他的专业网络.不幸的是,他是个外地人,还不认识社区中的任何人.但是他可以与 N 个人建立朋友关系 . 然而,社区里没几个人想与一个外地人交朋友.Kevin 想交朋友的 N ...

  9. 小A的树 - 树形DP

    题面 1 9 4 4 1 1 5 1 2 3 2 3 6 6 7 6 8 9 6 0 1 0 1 0 0 1 0 1 3 2 7 3 4 0 9 5 YES YES NO NO 题解 n <= ...

  10. flutter系列之:移动端的手势基础GestureDetector

    目录 简介 Pointers和Listener GestureDetector 手势冲突 总结 简介 移动的和PC端有什么不同呢?同样的H5可以运行在APP端,也可以运行在PC端.两者最大的区别就是移 ...