1、什么是线程安全 

  线程安全是编程中的术语,指某个函数、函数库在多线程环境中被调用时,能够正确地处理多个线程之间的共享变量,使程序功能正确完成。
一般来说,线程安全的函数应该为每个调用它的线程分配专门的空间,来储存需要单独保存的状态(如果需要的话),不依赖于“线程惯性”,把多个线程共享的变量正确对待(如,通知编译器该变量为“易失(volatile)”型,阻止其进行一些不恰当的优化),而且,线程安全的函数一般不应该修改全局对象。(注:摘自https://www.baidu.gugeeseo.com/wiki/%E7%BA%BF%E7%A8%8B%E5%AE%89%E5%85%A8

  这里有两个前提:

  1、多线程环境

  2、线程直接共享变量

  在这个前提下,我们才可以讨论线程安全问题。那什么是线程安全?如果上边的文字还不能理解,请看以下示例:

  

     private int number = ;
public void TestVolatile()
{
for (var i = ; i < ; i++)
{
number++;
}
Console.WriteLine(number);
}

输出结果:100

这个结果和我们预期的一直,但这是单线程环境,现在我们使用多线程环境

     public void Test()
{
for (var i = ; i < ; i++)
{
TestVolatile();
}
} public void TestVolatile()
{
int number = ;
var tasks = new List<Task>();
for (var i = ; i < ; i++)
{
var task = Task.Run(() =>
{
number++;
});
tasks.Add(task);
}
Task.WaitAll(tasks.ToArray());
Console.WriteLine(number);
}

数据结果:

我们发现多线程环境下:

1、输出结果不是一个固定的值

2、输出结果和单线程环境结果不一致,和我们的预期不一致

通过上述两个示例,我们可以明确:示例一是线程安全的,示例二不是线程安全的

2、如何解决线程安全

  在思考如何解决线程安全问题前,我们先思考下示例二为什么会出现这种结果?

   首先,它肯定是执行了100次累加的,其次为什么结果会不是100呢?

   答案只有一个:脏读了,就是说:我同时多个线程读取到的number相同,大家都加了1在赋值给number,导致结果就是看上去只有一个加了1。

   所以根据这个问题,我们解决的思路就是多线程环境下对number++执行应该是单线程的,同时只能有一个线程在执行此段代码。

   所以想要解决线程安全问题,其实就是解决执行代码段原子性问题,确保同时只有一个线程在访问,那我们就说这段代码是线程安全的。在.NET中线程安全问题主要可以通过以下方式处理,它们各自适用的环境不一样,需要了解其机制酌情使用:

对象名/关键字 方法 说明 示例
lock(关键字)  

通过获取指定对象的排它锁,达到一段代码块线程安全的执行;

lock本身是个语法糖,底层是通过Monitor来实现的;

        private static object _LOCK = new object();

        public void Main()
{
Monitor.Enter(_LOCK);
try
{
//执行
}
catch { }
finally
{
Monitor.Exit(_LOCK);
}
}
        private static object _LOCK = new object();

        public void Main()
{
lock (_LOCK)
{ }
}
volatile(变量修饰符)  

volatile是C#中用于控制同步的关键字,其意义是针对程序中一些敏感数据,不允许多线程同时访问,保证数据在任何访问时刻,最多有一个线程访问,以保证数据的完整性,volatile是修饰变量的修饰符。

多个线程同时访问一个变量,CLR为了效率,允许每个线程进行本地缓存,这就导致了变量的不一致性。volatile就是为了解决这个问题,volatile修饰的变量,不允许线程进行本地缓存,每个线程的读写都是直接操作在共享内存上,这就保证了变量始终具有一致性。

        private volatile bool _isCompleteCalled;
static 构造函数  

将构造函数申明未静态的,这是利用的C# 类加载机制实现的。其只会在类第一次实例化时执行,优先级高于非静态构造函数;

 public class TestLock
{
static TestLock()
{
//在第一次实例化的时候,仅执行一次
}
}
Interlocked(为多个线程共享的变量提供原子操作) Add 对两个整数进行求和并用和替换第一个整数,上述操作作为一个原子操作完成。  
CompareExchange 比较两个对象 T 是否相等,如果相等,则替换第一个。  
Decrement 以原子操作的形式递减指定变量的值并存储结果  
Exchange  以原子操作的形式,将对象设置为指定的值并返回对原始对象的引用。  
Increment 以原子操作的形式递增指定变量的值并存储结果  
LazyInitializer(提供延迟初始化例程) EnsureInitialized 初始化具有类型的默认构造函数的目标引用类型,如果其尚未已初始化。  
Monitor(提供同步访问对象的机制) Enter 在指定对象上获取排他锁  
  Exit 释放指定对象上的排他锁  
  IsEntered 确定当前线程是否保留指定对象上的锁  
  Pulse 通知等待队列中的线程锁定对象状态的更改  
  PulseAll 通知所有的等待线程对象状态的更改  
  TryEnter 在指定的时间内尝试获取指定对象上的排他锁  
  Wait 释放对象上的锁并阻止当前线程,直到它重新获取该锁。 如果已用指定的超时时间间隔,则线程进入就绪队列。 此方法还指定是否在等待之前退出上下文的同步域(如果在同步上下文中)然后重新获取该同步域。  
System.Collections.Concurrent.BlockingCollection<T>   为实现 System.Collections.Concurrent.IProducerConsumerCollection`1 的线程安全集合提供阻塞和限制功能。  
System.Collections.Concurrent.ConcurrentBag<T>   表示对象的线程安全的无序集合。  
System.Collections.Concurrent.ConcurrentDictionary<TKey, TValue>   表示可由多个线程同时访问的键/值对的线程安全集合。  
System.Collections.Concurrent.ConcurrentQueue<T>   表示线程安全的先进先出 (FIFO) 集合。  
System.Collections.Concurrent.ConcurrentStack<T>   表示线程安全的后进先出 (LIFO) 集合。  

.NET多线程之线程安全,Lock(锁)、Monitor(同步访问)、LazyInitializer(延迟初始化)、Interlocked(原子操作)、static(静态)构造函数、volatile、的更多相关文章

  1. JAVA多线程(三) 线程池和锁的深度化

    github演示代码地址:https://github.com/showkawa/springBoot_2017/tree/master/spb-demo/spb-brian-query-servic ...

  2. Java多线程(一)——线程基础和锁锁锁

    目录 Java多线程(一) 一.线程的定义 二.Synchronize线程同步 三.偏向锁.自旋锁.重量级锁 四.volatile关键字 五.Compare And Swap无锁自旋优化技术和ABA版 ...

  3. 多线程同步与并发访问共享资源工具—Lock、Monitor、Mutex、Semaphore

    “线程同步”的含义   当一个进程启动了多个线程时,如果需要控制这些线程的推进顺序(比如A线程必须等待B和C线程执行完毕之后才能继续执行),则称这些线程需要进行“线程同步(thread synchro ...

  4. 线程高级篇-Lock锁实现生产者-消费者模型

    Lock锁介绍: 在java中可以使用 synchronized 来实现多线程下对象的同步访问,为了获得更加灵活使用场景.高效的性能,java还提供了Lock接口及其实现类ReentrantLock和 ...

  5. Java中的Lock锁

    Lock锁介绍: 在java中可以使用 synchronized 来实现多线程下对象的同步访问,为了获得更加灵活使用场景.高效的性能,java还提供了Lock接口及其实现类ReentrantLock和 ...

  6. 并发编程系列之Lock锁可重入性与公平性

    一.相似之处:Lock锁 vs Synchronized 代码块 Lock锁是一种类似于synchronized 同步代码块的线程同步机制.从Java 5开始java.util.concurrent. ...

  7. 多线程 - 线程同步锁(lock、Monitor)

    1. 前言 多线程编程的时候,我们不光希望两个线程间能够实现逻辑上的先后顺序运行,还希望两个不相关的线程在访问同一个资源的时候,同时只能有一个线程对资源进行操作,否则就会出现无法预知的结果. 比如,有 ...

  8. 重新想象 Windows 8 Store Apps (46) - 多线程之线程同步: Lock, Monitor, Interlocked, Mutex, ReaderWriterLock

    [源码下载] 重新想象 Windows 8 Store Apps (46) - 多线程之线程同步: Lock, Monitor, Interlocked, Mutex, ReaderWriterLoc ...

  9. C#多线程系列(2):多线程锁lock和Monitor

    1,Lock lock 原型 lock 编写实例 2,Monitor 怎么用呢 解释一下 示例 设置获取锁的时效 C# 中,可以使用 lock 关键字和 Monitor 类来解决多线程锁定资源和死锁的 ...

随机推荐

  1. 规则“Microsoft Visual Studio 2008 的早期版本”失败。此计算机上安装了 Microsoft Visual Studio 2008 的早期版本。请在安装 SQL Server 2008 前将 Microsoft Visual Studio 2008 升级到 SP1。

    今天重装了一下系统后,需要装开发工具,我用的开发工具是Visual Studio2008 和SQL Server2008R2,装完Visual Studio2008的时候在装数据库的时候却出现这样的问 ...

  2. Android零基础入门第89节:Fragment回退栈及弹出方法

    在上一期分享的文章末尾留了一个课后作业,有去思考如何解决吗?如果已经会了那么恭喜你,如果还不会也没关系,本期一起来学习. 一.回退栈 在前面两期的示例中,当我们完成一些操作后,如果想要回到操作之前的状 ...

  3. A simple in-process HTTP server for UWP

    原文 http://www.dzhang.com/blog/2012/09/18/a-simple-in-process-http-server-for-windows-8-metro-apps 简单 ...

  4. delphi Stomp客户端连接 RabbitMQ(1)

    最近公司想上个消息推送系统,网上搜了很多,因公司主要产品是Delphi,我选择了开源的RabbitMQ,Erlang语言开发,天生并行. 代码下载地址:delphistomp下载地址 windows上 ...

  5. Qt中连接到同一signal的多个slots的执行顺序问题(现在是看连接顺序,以前是无序的)

    in the order they have been connected 起源 前些天忘记在哪儿讨论过这个问题,今天在csdn又看到有网友问这个问题,而其他网友却无一例外的给出了“无序”这个答案. ...

  6. GO :互联网时代的 C 语言!

    摘要: 每周为您推送最有价值的开源技术内参! 技术干货 标签:独家译文 1.Go 很好,为什么我们不使用它? 在这篇文章中,我将分享一下为什么我认为它很棒,使用它的一些缺点,以及为什么它还不是我们 Z ...

  7. QT编译./configure参数的详细解释

    可以随便的转载,只要按照规矩走带上咱论坛的链接就好. ======================================全文是按照./configure -help来翻译的========= ...

  8. QThread多线程编程经典案例分析(三种方法,解释了为什么使用moveToThread的根本原因,即为了避免调用QThread::exec() )

    传统的图形界面应用程序都只有一个线程执行,并且一次执行一个操作.如果用户调用一个比较耗时的操作,就会冻结界面响应. 一个解决方法是按照事件处理的思路: 调用 Void QApplication::pr ...

  9. UbuntuServer添加软件源列表

    要使用Ubuntu前,我们一般都要先做好工具!特别是对于安装这一块~~~~ 1.配置前,先做个配置文件的备份: $sudo cp /etc/apt/sources.list /etc/apt/sour ...

  10. return view 详解 MVC

    1.return View(); 返回值 类型:System.Web.Mvc.ViewResult将视图呈现给响应的 View() 结果. 注释 View() 类的此方法重载将返回一个具有空 View ...