.NET中的中并发编程 | 您所在的位置:网站首页 › 异步编程和多线程 › .NET中的中并发编程 |
在面试过程中,面试官常常会讨论多线程问题,在开发过程中,也有很多工作我们都需要去开线程来解决。.NET在多线程编程上提供了多种API,线程、异步、任务、并行计算这些又有什么异同,该如何选择呢,今天让我们来讨论一下。 首先,我们要给几个概念做一个简单定义。 并发编程:并发同时做多件事情。 多线程:多线程是并发的一种形式,它采用多个线程来执行程序。 异步编程:并发的一种形式,采用回调(callback)机制,以避免产生不必要的线程。 响应式编程:一种声明式的编程模式,程序在该模式中对事件做出响应。 并行处理:把正在执行的大量的任务分割成小块,分配给多个同时运行的线程。 首先,我们需要明确我们为什么需要并发编程,来看这样一个例子: 假设你有一个网站, 需要嵌套一个广告, 如果是顺序执行的, 那这个广告的加载缓慢就会使得请求一直等待,使得等待期间正常页面的无法显示,也就是所谓的网页卡慢。但如果我们在向广告服务器发起请求后, 直接开始加载其他页面, 直到广告服务器返回结果后再开始显示广告。 一.多线程和异步的异同:需要实现向广告服务器发起请求后, 直接开始加载其他页面。我们可以考虑这样有一个方案,在主线程中加载页面,单独通知CPU开启一个线程来加载广告,这就是所谓的多线程。.net中以thread相关的语法为主,这里不做赘述。 虽然单独开启一个线程来加载广告解决我们既要加载广告又不影响其他页面加载。但是这种方式是高效的吗?答案是否定的。CPU开启的线程在网络加载过程中一直处于被占用其实是一种浪费。 要理解这个问题我们要从两个角度来阐述, 一是从加载页面的角度来说,一个页面包括加载网络请求,页面的展示等。二是其实我们的电脑主机的硬件里面,除了CPU外,还存在其他硬件进行IO操作时,还存在不经过CPU的数据交换模式,比如网络请求中可以采用网卡中的DMA模式来转移CPU的压力。这个时候就需要我们采用异步模式,它的底层使用线程池进行管理,异步操作启动时,CLR会将下载网页操作这部分工作丢给线程池中的某个线程去执行。当开始IO操作时,异步会把工作线程还给线程池,这时候就相当于下载网页的这个工作不会再占用CPU资源了。 明白了多线程和异步的区别后,我们来确定下二者的具体使用场景:CPU密集型采用多线程; I/O密集型采用异步。 如果你区分不来什么是CPU密集型还是I/O密集型的,你就记住一点:涉及到任何读写操作和数据传输有关的,就属于I/O密集型,否则就是CPU密集型,也叫计算密集型。 二.线程的同步与阻塞前段时间我同事遇到这样一个问题:连续用了两个异步请求去获取资源反而导致页面的加载更慢了。为什么发生会采用多线程反而会更慢了呢? 原来是在进行异步请求时访问了session,为了安全考虑(避免单个用户并发请求导致的session数据错乱),.net中采用排它锁来互斥的访问session数据,也就是说开启了session后,单个用户只能串行的访问这个站点,直到这个访问结束前,其他的请求都会进入等待状态,从多线程变为单线程。 这就是所谓线程同步,就是多线程访问共享资源时的一种等待(也可以理解为锁定某个对象),直到该共享资源被解除锁定。加锁就是很常见的线程同步方式。 其他请求的等待状态就称为阻塞。上一篇《干净代码的基本原则》给新入行朋友的建议里提到的 “async/await用到底,不要穿插用result。” 也是基于阻塞的原因。Task的Result属性可以拿到线程执行完返回的值,同时阻塞线程直到拿到返回的结果,这样会在阻塞当前线程同时仍然会启动另外一个后台线程去执行这个函数,得不偿失。 先这样,下次再写吧 |
CopyRight 2018-2019 实验室设备网 版权所有 |