前言:

在计算机网络通讯中有两种传输方式。同步传输和异步传输。同步传输好比一座单行的高架桥,
异步传输好比一座双通道的高架桥。同样在程序代码中也存在同步和异步的执行方式。先来了解一下程序的中几个概念

进程和线程

线程与进程相比更轻量,而且线程之间是共享内存堆栈的,所以不同的线程之间交互非常容易实现。比如聊天室这样的程序,客户端连接之间可以交互,比聊天室中的玩家可以任意的其他人发消息。用多线程模式实现非常简单,线程中可以直接向某一个客户端连接发送数据。而多进程模式就要用到管道、消息队列、共享内存,统称进程间通信(IPC)复杂的技术才能实现。

线程是调度的基本单位,而进程则是资源拥有的基本单位。

在计算机编程中,进程(Process)和线程(Thread)是两个重要的概念,用于管理和执行程序的并发性和并行性。

进程是指计算机中正在运行的程序的实例。每个进程都有自己的地址空间、内存、文件描述符、环境变量等。进程之间是相互独立的,它们通过操作系统进行管理和调度。每个进程都会被分配一个唯一的进程标识符(PID)。

进程可以看作是一个独立的执行环境,它包含了程序的代码和相关的资源。每个进程都在自己的地址空间中运行,所以进程之间的数据隔离较好。进程之间通过进程间通信(IPC)机制来进行通信和共享数据。

线程是进程内的执行单位。一个进程可以包含多个线程,它们共享相同的地址空间和资源。线程拥有自己的程序计数器、寄存器集合和栈,但与同一进程的其他线程共享堆内存、全局变量和文件描述符等。

线程是轻量级的执行单位,它可以与其他线程并发执行,从而实现并行性。在多核处理器上,多个线程可以同时在不同的核上执行,提高系统的整体性能。

线程之间的切换比进程之间的切换更快,因为线程共享相同的上下文和资源。但也因为共享资源,线程之间需要注意同步和互斥,以避免数据竞争和并发问题。

总结来说,进程是独立的执行环境,而线程是进程内的执行单位。进程之间相互独立,线程之间共享相同的资源。进程之间切换开销较大,线程之间切换开销较小。进程和线程的合理使用可以提高程序的并发性和性能。

在了解了进程和线程后,我们再来了解阻塞和非阻塞的概念

阻塞、非阻塞 I/O 与同步、异步 I/O 的区别和联系

首先我们来看阻塞和非阻塞 I/O。根据应用程序是否阻塞自身运行,可以把 I/O 分为阻塞 I/O 和非阻塞 I/O。
所谓阻塞I/O,是指应用程序在执行I/O操作后,如果没有获得响应,就会阻塞当前线程,不能执行其他任务。
所谓非阻塞I/O,是指应用程序在执行I/O操作后,不会阻塞当前的线程,可以继续执行其他的任务。
再来看同步 I/O 和异步 I/O。根据 I/O 响应的通知方式的不同,可以把文件 I/O 分为同步 I/O 和异步 I/O。
所谓同步 I/O,是指收到 I/O 请求后,系统不会立刻响应应用程序;等到处理完成,系统才会通过系统调用的方式,告诉应用程序 I/O 结果。
所谓异步 I/O,是指收到 I/O 请求后,系统会先告诉应用程序 I/O 请求已经收到,随后再去异步处理;等处理完成后,系统再通过事件通知的方式,告诉应用程序结果。
你可以看出,阻塞/非阻塞和同步/异步,其实就是两个不同角度的 I/O 划分方式。它们描述的对象也不同,阻塞/非阻塞针对的是 I/O 调用者(即应用程序),而同步/异步针对的是 I/O 执行者(即系统)。

协程是什么

首先协程的最简定义是用户态线程,它不由操作系统而是由用户创建,跑在单个线程(核心)上,比进程或是线程都更加轻量化,通常创建它只有内存消耗:假如你的配置允许你开几千个进程或线程,那么开几万个几十万个协程也是很轻松的事情,只要内存足够大,你可以几乎无止境地创建新的协程。在Swoole下,协程的切换实现是依靠双栈切换,即C栈和PHP栈同时切换,由于有栈协程的上下文总是足够的小,且在用户态便能完成切换,它的切换速度也总是远快于进程、线程,一般只需要纳秒级的CPU时间,对于实际运行的逻辑代码来说这点开销总是可以忽略不计(尤其是在一个重IO的程序中,通过调用分析可以发现协程切换所占的CPU时间非常之低)。

Go 语言中没有线程的概念,只有协程,也称为 goroutine。相比线程来说,协程更加轻量,一个程序可以随意启动成千上万个 goroutine。

goroutine 被 Go runtime 所调度,这一点和线程不一样。也就是说,Go 语言的并发是由 Go 自己所调度的,自己决定同时执行多少个 goroutine,什么时候执行哪几个。这些对于我们开发者来说完全透明,只需要在编码的时候告诉 Go 语言要启动几个 goroutine,至于如何调度执行,我们不用关心

异步编程一般使用回调方式,如果遇到非常复杂的逻辑,可能会层层嵌套回调函数。协程就可以解决此问题,可以顺序编写代码,但运行时是异步非阻塞的。腾讯的工程师基于Swoole扩展和PHP5.5的Yield/Generator语法实现类似于Golang的协程,项目名称为TSF(Tencent Server Framework),开源项目地址:https://github.com/tencent-php/tsf。目前在腾讯公司的企业QQ、QQ公众号项目以及车轮忽略的查违章项目有大规模应用 。

综上所述:

所以来说,异步并不是物理上实现。计算机中只有进程和线程的概念。没有协程的概念。我们
也可以把轻量级用户线程称之为协程。那很多小明就要问了,既然不是物理上实现,那PHP、Java、Python 这些语言都会有异步操作为嘛就Go 热度这么高咧。这个就说来话长,从PHP 和 Python 这两门脚本语言说起就一直争论不休,对比之下再来看GO语言,就会发现使用爬虫和大数据科学计算用python 语言,开发网站使用PHP语言,使用异步及协程使用Go 语言。这里最直观简单的规律,语言已经封装好了,python不用去单独写代码实现,而PHP要写代码逻辑。go 语言只需要使用Go 关键字就可以开启异步,其他语言得写一大堆代码,还容易出错。还有一个在互联网圈流行就是先入为主的关联

异步

异步是指在程序执行过程中,某个操作不会阻塞后续代码的执行。简而言之,异步操作不会按照顺序一步步执行,而是在后台进行,当操作完成时,通过回调函数、Promise对象、async/await等机制通知代码继续执行相关逻辑。

异步的优点

结合上面的介绍,既然异步的概念早就有了,为嘛直到GO问世后才人尽皆知咧。异步解决了问题是否是开发中的银弹。

  1. 提高性能和响应速度
    异步编程允许程序执行其他任务而不是等待某个操作完成。这在处理 I/O 操作(例如网络请求、文件读写)时尤为明显。通过异步操作,主线程能够继续执行其他任务,提高了程序的性能和响应速度。
  2. 提升用户体验
    在前端开发中,异步编程可用于处理用户交互和动画,使得页面更加流畅和响应。用户在进行操作时,页面不会被阻塞,提升了整体用户体验。

异步缺点

  1. 复杂性增加
    异步编程通常需要处理回调函数、Promise链、事件处理等复杂的代码结构,容易导致代码的可读性和可维护性下降。特别是在回调嵌套较深的情况下,产生了所谓的 "回调地狱"。
  2. 错误处理复杂
    在异步编程中,错误的处理相对复杂,需要注意在回调链中适当地捕获和处理错误,否则可能导致程序崩溃或难以调试。

问题一: 实验异步能提高查询MySQL的性能吗?
答案:不能,异步是代码逻辑实现的,查询SQL语句是MySQL服务器查询的。逻辑上代码实现更服务器的查询时间没有关联性。

MySQL异步 参考链接:MySQL的连接池、异步、断线重连

MySQL异步是指将MySQL连接事件驱动化,这样就变成了非阻塞IO。数据库操作并不会阻塞进程,在MySQL-Server返回结果时再执行对应的逻辑。

有几个点需要注意一下:

异步MySQL并没有节省SQL执行的时间
一个MySQL连接同时只能执行1个SQL,如果异步MySQL存在并发那么必须创建多个MySQL连接
异步回调程序中,异步MySQL并没有提升性能。异步最大的好处是可以高并发,如果并发1万个请求,那么就需要建立1万个MySQL连接,这会给MySQL-Server带来巨大的压力。

MySQL是根据连接数分配资源的,一个连接需要开启一个线程。1000连接那么需要维持1000线程才可以。线程数量增加后,线程间切换会占用大量CPU资源
MySQL短连接反而不会出现此问题,因为短连接在使用完后就释放了。不会占用MySQL-Server的连接资源

虽然应用层代码使用异步回调避免了自身的阻塞,实际上真正的瓶颈是数据库服务器。异步MySQL还带来了额外的编程复杂度,所以除非是特殊场景的需求,否则不建议使用异步MySQL。

如果程序中坚持要使用异步,那么必须是异步MySQL+连接池的形式。超过规定的MySQL最大连接后,应当对SQL请求进行排队,而不是创建新连接,避免大量并发请求导致MySQL服务器崩溃。

所以类似Redis、MySQL、Elasticsearch、Mogodb 使用异步能不能提高执行效率的问题同上。

抛开语言看问题,就好像 不识庐山真面目,只缘身在此山中


叶剑飞雪
137 声望9 粉丝