目录

每日八股Golang篇一概述

【每日八股】Golang篇(一):概述

进程、线程、协程的区别?

进程 :进程是每一次程序动态执行的过程,是程序运行的基本单位。进程占据独立的内存,有内存地址和自己的堆,挂靠与操作系统。操作系统以进程为单位进行资源分配,进程是资源分配的最小单位。

线程 :线程又称轻量级线程,是 CPU 调度的最小单元。线程隶属于进程,是程序的实际执行者,一个进程至少包含一个主线程,可以有多个子线程。线程会共享所属进程的资源,同时线程拥有自己的独占资源。线程切换和线程间通信主要通过内存共享(比如加锁),上下文切换很快(因为同一个进程内的线程共享资源),资源开销较少,但是相比于进程而言不够稳定,容易丢失数据。

协程 :协程是一种 用户态的轻量级线程 ,协程的调度完全由用户控制。一个线程可以有多个协程,协程不被操作系统内核管辖,而 是由用户控制

区别

  • 资源占用 :进程是操作系统分配资源的最小单位,因此线程本身不占有资源,它可以访问其所隶属的进程的资源。进程所维护的是程序所包含的静态资源,比如: 地址空间、打开的文件句柄集、文件系统状态、信号处理 等;线程维护的是与程序运行相关的资源,即动态资源,包括: 运行栈、调度相关的控制信息、待处理的信号集 等。【或者说,进程维护的是资源,线程维护的是状态】
  • 并发性 :不仅进程可以并发,同一个进程的多个线程也可以并发。
  • 系统开销 :在创建或撤消进程时,由于系统都要为之分配和回收资源,导致系统的开销明显大于创建或撤消线程时的开销。但是进程有独立的地址空间,一个进程崩溃后, 在保护模式下不会对其它进程产生影响 ,而线程只是一个进程中的不同执行路径。线程有自己的堆栈和局部变量,但线程之间没有单独的地址空间,一个进程死掉就等于所有的线程死掉,所以 多进程的程序要比多线程的程序健壮,但在进程切换时,耗费资源较大,效率要差一些
  • 协程和线程 :协程避免了无意义的调度,由此可以提高性能,但是用户调度的过程中存在风险。

goroutine 相比线程的优势?

Goroutine 相较于传统线程而言有以下优点:

  1. 轻量级内存占用,goroutine 的初始栈为 KB 级别,可动态拓展,而线程栈通常在 MB 级别,因此使用 goroutine 可以轻松创建百万级的 goroutines;
  2. 快速创建销毁:goroutine 的创建耗时约为 300ns,且由于完全由用户态管理,故无系统调用的开销;
  3. 高效调度机制:单个线程可处理数万 goroutine,上下文切换成本约 200ns;
  4. 智能处理阻塞:goroutine 阻塞时,运行时会自动创建其它新线程处理其它 goroutine,避免线程级阻塞导致的资源浪费;
  5. 通信更安全:goroutine 采用原生 channel 替代共享内存;
  6. 资源利用率优化;

典型对比数据

  • 10万并发连接:Goroutine 消耗≈200MB内存,线程模式需要≈100GB;
  • 上下文切换吞吐量:Go 调度器比 OS 调度器高 5-10倍;

go 与 Java 的区别?

  • 运行:go 是静态编译语言;Java 基于类的面向对象语言,Java 应用程序在 JVM 上运行。
  • 函数重载:go 上不允许函数重载,必须具有方法和函数的唯一名称;java 允许函数重载。
  • 多态:Java 默认允许多态,而 go 没有。
  • 路由配置:go 语言使用 HTTP 协议进行路由配置;java 使用 Akka.routing 进行路由配置。
  • 继承:go 的继承 通过匿名组合完成 (也就是 struct 当中的嵌入),基类以 Struct 的方式定义,子类只需要把基类作为成员放在子类的定义中,支持多继承;Java 的继承通过 extends 关键字完成,不支持多继承。

go 如何实现继承?

通过 struct 当中的嵌入来实现类似于继承的效果。

init 函数什么时候执行?

特点

  • init函数先于main函数自动执行,不能被其他函数调用。
  • init函数没有输入参数、返回值。
  • 每个包可以有多个init函数,包的每个源文件也可以有多个init函数。
  • go没有明确定义同一个包的init执行顺序,编程时程序不能依赖这个执行顺序。
  • 不同包的init函数按照包导入的依赖关系决定执行顺序。

作用

  • 初始化不能采用初始化表达式初始化的变量。
  • 程序运行前的注册。
  • 实现sync.Once功能。

执行顺序

go程序初始化先于main函数执行,由runtime进行初始化,初始化顺序如下:

  • 初始化导入的包,包的初始化顺序并不是按导入顺序执行的,runtime需要解析包依赖关系,没有依赖的包最先初始化。
  • 初始化包作用域的变量,runtime解析变量依赖关系,没有依赖的变量最先初始化。
  • 执行包的init函数。

最终的初始化顺序:变量初始化

→ \rightarrow

init()

→ \rightarrow

main()