.NET异步程序设计——基本概念

shanzm-2020-02-10 20:56

0.为什么要异步编程

“有些程序操作会花费较长时间。设想一下这样的几种情况:一个单线程程序调用一个远程对象的方法,或是调用一个执行耗时数据库查询的方法,或是下载一个大文档,或是向一个外部文件写500行的文字。在执行这些操作时,应用程序会显得挂起很长时间。在任务完成之前,这个程序的其他部分(比如菜单激活、工具条的单击或者控制台的输出)都会挂起(这会让用户不耐烦)。” --《精通C#(第六版)》

因为上述情形,我们希望可以多个任务同时处理(即并行处理),所以需要异步编程



1.进程与线程

  1. 进程

    什么是进程?

    狭义定义:进程(process),是指计算机中已运行的程序。

    广义定义:进程是一个具有一定独立功能的程序关于某个数据集合的一次运行活动。它是操作系统动态执行的基本单元,在传统的操作系统中,进程既是基本的分配单元,也是基本的执行单元。

    在面向线程设计的系统(如当代多数操作系统、Linux 2.6及更新的版本)中,进程本身不是基本运行单位,而是线程的容器,每一个进程中的线程共享资源。

    程序本身只是指令、数据及其组织形式的描述,进程才是程序(那些指令和数据)的真正运行实例。

  2. 线程

    什么是线程?

    线程(thread)是操作系统能够进行运算调度的最小单位。

    大部分情况下,它被包含在进程之中,是进程中的实际运作单位。

    一条线程指的是进程中一个单一顺序的控制流,一个进程中可以并发多个线程,每条线程并行执行不同的任务。

    线程是Windows进程中的独立执行单元,每个线程都有一个主线程,并且每个线程还可以以程序方式建立新的线程。

    在.net 程序中,从主函数Main()执行时,CLR建立主线程,该线程可以使用Thread类创建其他的线程,被新创建的线程则称为次线程

    • 区别进程和线程:"进程是cpu资源分配的最小单位,线程是cpu调度的最小单位。以前进程既是资源分配也是调度的最小单位,后来为了更合理的使用cpu(实际上是cpu性能越来越好),才将资源分配和调度分开,就有了线程。线程是建立在进程的基础上的一次程序运行单位。"

    • 进程是CPU资源分配的基本单位,进程与进程之间是隔离的,同一个进程中的线程共享同一个执行环境(特别是内存)。

      • 注意:CLR为每个一线程分配独自的栈空间,因此同一个进程中的每一个线程中的局部变量是独立。
  3. 多线程

    为什么要多线程?

    如果这个线程正在执行复杂的长时间任务(输出一个大文件、连接至数千公里以外的服务器访问大量数据),

    那么这个线程所在进程(尤其是GUI程序)对用户端而言会显示得像没有响应一样(连任务管理器中都可能显示“没有响应”)。

    这对于单线程程序是个明显的缺点。

    因此可以想到,可以使用多线程改善程序的响用

    上面为什么使用多线程中说过,因为会出现需要长时间的复杂任务,使用户等待,所以我们要使用多线程。

    这里提到的长时间的复杂任务又分为两种:

    • 如果一个任务大部分时间都在等待某个条件的发生,那么就称之为I/OBound任务,即“I0密集型

      例如,网页呈现之前CSS、JS与图片的下载,Console.Read方法(I/O Bound任务一般都会与输入或输出有关,但不是绝对,例如,Thread.Sleep方法是一种I/OBound);

    • 如果一个任务大部分时间都在执行CPU运算,那么就称为Compute Bound(也称CPU Bound)即“CPU密集型”。

  4. 线程的分配

    注意当你定义一个线程,操作系统会自动给没一个线程分配一个"时间片段",定义并启动线程是需要一定的时间成本的,所以这个定义并启动线程的操作并不是实时的。

    所以会出现这种状况:后定义后启动的线程会有可能比先定义先启动的程序先执行。

    线程的分配过程是非常复杂的,具体我也没深入了解,但是有一点要知道:线程的分配和启动是需要一定的时间和成本的。
    点击:示例2

  5. 并发问题

    首先:多线程编程几乎是无法控制底层操作系统和CLR对线程的调度

    一个特定的线程在它的生命周期中并不会被限定在一个程序域,线程通过CLR可以在应用程序域和上下文边界移动(CLR是控制线程移入和移除应用程序域和上下文的实体)。所以应该留意应用程序中会出现线程的不稳定操作。

    比如说:“一个线程对某个数据对象进行操作,还没有结束的时候,另外一个线程也访问到该对象的数据,此时线程调度程序会发出指令挂起第一线程,而只是第一个线程还没有全部完成对该数据对象的操作,那么后来的的那个线程会看到的只是部分修改的数据对象,这样它读到的数据基本就是虚假的,从而出现一些难以复现的bug ” --摘编自《精通C#(第六版)》

  6. 线程安全的概念

    • 非线程安全:在运行中不提供数据访问保护,这就可能导致多个线程先后更改数据,最后所得的数据是脏数据。
    • 线程安全:多线程访问时,采用了加锁机制,当一个线程访问该类的某个数据时,进行保护,其他线程不能进行访问直到该线程读取完,其他线程才可使用。不会出现数据不一致或者数据污染
      • 单线程则同一时间内只有一个线程操作数据,故单线程是线程安全的。


2.同步与异步概念

  • 同步程序设计(Synchronous Programming):程序的执行按照从上往下的顺兴执行,简单的说就是step by step。

    我们平时编写和调用的方法默认都是同步的。

  • 异步程序设计(Asynchronous Programming):程序的执行是无序或并行的。

    我们编写异步方法后,在调用该异步方法的时候(此处调用异步方法的那个方法就是调用方法(calling method)),在该异步方法尚未完成其工作前就返回到调用方法,调用方法中继续执行后续代码的同时,被调用的异步方法继续完成其工作。

    因为异步方法可以非常快的返回到调用者,所以异步方法也称为非锁定方法

    简而言之:异步就是调用一个方法,调用者不用等待该方法结果的返回,可以继续做其他的事情。

  • 说明:我看到书中说第一次看到两个概念的时候觉得同步编程和异步编程好像和字面的意思相反,会让人迷糊,

    其实是同步程序设计是的不同步(即按顺序从上到下执行的),异步程序设计是并行的,是同步的。

    但是我觉得你若是理解Ajax,可以想想使用Ajax实现异步刷新!



3.多线程与异步之间的关系

异步是目的,多线程是实现异步的方法

异步和多线程是两个概念,异步与之对应是的同步,多线程与之对应是的单线程

多线程强调的是对CPU的高效利用,强调的是并发

异步强调的是非阻塞,异步强调的是函数的非阻塞

《网站开发之美-7.6.4》关于WEB开发中多线程和异步的区分:CPU密集型适合多线程,IO密集型适合异步



4.参考书籍

书籍:《ASP.NET MVC5 网站开发之美》推荐这本书,台湾的几个作者编写的,条理清晰,简单明了

书籍:《C# 5.0图解教程》 注意之前的版本的该书籍是没有异步编程的章节的,该书最新版是去年的下半年人邮出版的《C# 6.0图解教程》

书籍:《C#7.0核心技术指南》

posted @ 2020-02-10 20:56  shanzm  阅读(349)  评论(0编辑  收藏  举报
TOP