new 的作用是初始化一个指向类型的指针(*T)
new函数是内建函数,函数定义:func new(Type) *Type
使用new函数来分配空间。传递给new 函数的是一个类型,不是一个值。返回值是 指向这个新分配的零值的指针。
make 的作用是为 slice,map 或 chan 初始化并返回引用(T)。
make函数是内建函数,函数定义:func make(Type, size IntegerType) Type
- 第一个参数是一个类型,第二个参数是长度
- 返回值是一个类型
make(T, args)
函数的目的与new(T)不同。它仅仅用于创建 Slice, Map 和 Channel,并且返回类型是 T(不是T*)的一个初始化的(不是零值)的实例。
都是把格式好的字符串输出,只是输出的目标不一样: Printf(),是把格式字符串输出到标准输出(一般是屏幕,可以重定向)。 Printf() 是和标准输出文件(stdout)关联的,Fprintf 则没有这个限制. Sprintf(),是把格式字符串输出到指定字符串中,所以参数比printf多一个char*。那就是目标字符串地址。 Fprintf(), 是把格式字符串输出到指定文件设备中,所以参数笔printf多一个文件指针FILE*。主要用于文件操作。Fprintf()是格式化输出到一个stream,通常是到文件。
-
(1). 数组 数组是具有固定长度且拥有零个或者多个相同数据类型元素的序列。 数组的长度是数组类型的一部分,所以[3]int 和 [4]int 是两种不同的数组类型。
-
数组需要指定大小,不指定也会根据初始化的自动推算出大小,不可改变 ;
-
数组是值传递;
-
数组是内置(build-in)类型,是一组同类型数据的集合,它是值类型,通过从0开始的下标索引访问元素值。在初始化后长度是固定的,无法修改其长度。当作为方法的参数传入时将复制一份数组而不是引用同一指针。数组的长度也是其类型的一部分,通过内置函数len(array)获取其长度。
-
数组定义:
-
var array [10]int
-
var array = [5]int{1,2,3,4,5}
-
-
-
(2). 切片 切片表示一个拥有相同类型元素的可变长度的序列。 切片是一种轻量级的数据结构,它有三个属性:指针、长度和容量。
-
切片不需要指定大小;
-
切片是地址传递;
-
切片可以通过数组来初始化,也可以通过内置函数make()初始化 .初始化时len=cap,在追加元素时如果容量cap不足时将按len的2倍扩容;
-
切片定义:
- var slice []type = make([]type, len)
-
- go env: #用于查看go的环境变量
- go run: #用于编译并运行go源码文件
- go build: #用于编译源码文件、代码包、依赖包
- go get: #用于动态获取远程代码包
- go install: #用于编译go文件,并将编译结构安装到bin、pkg目录
- go clean: #用于清理工作目录,删除编译和安装遗留的目标文件
- go version: #用于查看go的版本信息
协程和线程都可以实现程序的并发执行;
通过channel来进行协程间的通信;
只需要在函数调用前添加go关键字即可实现go的协程,创建并发任务;
关键字go并非执行并发任务,而是创建一个并发任务单元;
方法施加的对象显式传递,没有被隐藏起来
golang的面向对象表达更直观,对于面向过程只是换了一种语法形式来表达
方法施加的对象不需要非得是指针,也不用非得叫this
数组切片、字典(map)、通道(channel)、接口(interface)
-
切片(Slice):
切片是对数组的引用,具有动态大小。切片包含一个指向底层数组的指针、切片的长度以及容量。
切片在传递给函数或赋值时,传递的是对底层数组的引用,而不是数组的副本。
-
字典(Map):
字典是一种键值对的数据结构,类似于其他语言中的哈希表或字典。
Map的底层实现是引用类型,因此在函数间传递时,是通过引用传递的。
-
通道(Channel):
通道用于在Goroutine之间传递数据。
通道本身是引用类型,传递的是对通道的引用。
-
指针(Pointer):
指针是对变量地址的引用。
通过指针可以间接地访问或修改变量的值。
-
接口(Interface):
接口定义了一组方法的集合。
接口变量实际上是一个包含了类型信息和类型值的双重指针结构,因此在赋值或传递时也是引用类型的行为。
-
(1) 当一个goroutine获得了Mutex后,其他goroutine就只能乖乖的等待,除非该goroutine释放这个Mutex
-
(2) RWMutex在读锁占用的情况下,会阻止写,但不阻止读
-
(3) RWMutex在写锁占用情况下,会阻止任何其他goroutine(无论读和写)进来,整个锁相当于由该goroutine独占
-
通信与同步机制
-
安全的数据传输: channel 是 Go 语言中用于在不同 goroutine 之间进行通信和同步的机制,它提供了一种安全、高效的方式来传递数据,避免了并发访问数据时的竞态条件和死锁等问题,确保数据的完整性和一致性。
-
同步等待: 无缓冲 channel 在发送和接收操作时会阻塞当前 goroutine,直到对应的接收或发送操作准备好,从而实现了数据的同步等待,保证数据在发送和接收时的原子性和顺序性,数据的发送和接收是一对一的同步关系,先发送的数据会先被接收。
-
-
类型安全
channel 只能传递指定类型的数据,在创建 channel 时需要指定其元素类型,如chan int表示只能传递整数类型数据的 channel,chan string只能传递字符串类型数据,这使得在编译阶段就能发现类型不匹配的错误,增强了程序的可靠性。
-
阻塞特性
-
发送阻塞: 对于无缓冲 channel,当发送数据时,如果没有对应的接收者准备好接收数据,发送操作会被阻塞;对于缓冲 channel,当缓冲区已满时,发送操作也会被阻塞,直到缓冲区有空闲空间。
-
接收阻塞: 无缓冲 channel 在没有数据可接收时,接收操作会被阻塞;缓冲 channel 在缓冲区为空时,接收操作会被阻塞,直到有数据可供接收。
-
-
缓冲功能
-
缓冲机制: 可以创建带缓冲的 channel,它可以在一定程度上存储数据,在发送端和接收端之间暂存一定数量的数据,从而减少阻塞,提高并发处理的效率。定义时通过指定第二个参数来设置缓冲区大小,如ch := make(chan int, 10)表示创建一个可以缓存 10 个整数的 channel。
-
非阻塞发送与接收: 在缓冲 channel 未满时,发送操作可以立即完成而不会阻塞;在缓冲 channel 未空时,接收操作也可以立即完成。
-
-
方向限制
可以限定 channel 只能进行发送或接收操作,从而提高代码的可读性和安全性。通过在定义 channel 类型时使用chan<-表示只能发送的单向 channel,<-chan表示只能接收的单向 channel,如func senddata(ch chan<- int)表示该函数的参数 ch 是一个只能发送整数的 channel。
-
可关闭性
发送者可以关闭一个 channel 来表示没有更多的值会被发送,接收者可以通过额外的接收参数来检查 channel 是否已经关闭,如v, ok := <-ch,如果ok为true,则表示v是从 channel 中接收的值,如果ok为false,则表示 channel 已经关闭,并且没有更多的值可接收。
-
支持多路复用
可以使用select语句同时监听多个 channel,一旦某个 channel 准备好进行数据交换,就会执行对应的操作,从而更加灵活地处理并发任务,实现对多个 channel 的并发处理和协调。
主要特性
-
多路复用:
select允许一个Goroutine等待多个通道操作。只要其中一个通道操作完成,select就可以继续进行。
-
随机选择:
如果有多个通道操作同时就绪,select会随机选择一个执行。这种随机选择机制有助于避免通道操作的偏向性。
-
阻塞行为:
如果所有通道都没有准备好,select将阻塞,直到有一个通道可以进行操作。
可以通过在select中使用default子句来避免阻塞,此时select会立即执行default中的代码块。
-
超时处理:
通过select与time.After结合,可以实现超时处理。例如,等待某个操作不超过指定的时间。
使用场景
-
同时等待多个通道操作:
select可以等待多个通道的读写操作,这在需要从多个信号源接收数据时特别有用。
-
实现超时功能:
通过select和time.After,可以在一定时间内等待通道操作,否则执行超时逻辑。
-
非阻塞通信:
使用select的default分支,可以实现非阻塞的通道操作。
-
处理多个Goroutine的结果:
在需要从多个并发任务中收集结果时,select可以帮助从多个通道中读取数据。
概念
-
进程: 进程是具有一定独立功能的程序关于某个数据集合上的一次运行活动,进程是系统进行资源分配和调度的一个独立单位。每个进程都有自己的独立内存空间,不同进程通过进程间通信来通信。由于进程比较重量,占据独立的内存,所以上下文进程间的切换开销(栈、寄存器、虚拟内存、文件句柄等)比较大,但相对比较稳定安全。
-
线程: 线程是进程的一个实体,是 CPU 调度和分派的基本单位,它是比进程更小的能独立运行的基本单位.线程自己基本上不拥有系统资源,只拥有一点在运行中必不可少的资源(如程序计数器,一组寄存器和栈),但是它可与同属一个进程的其他的线程共享进程所拥有的全部资源。线程间通信主要通过共享内存,上下文切换很快,资源开销较少,但相比进程不够稳定容易丢失数据。
-
协程: 协程是一种用户态的轻量级线程,协程的调度完全由用户控制。协程拥有自己的寄存器上下文和栈。协程调度切换时,将寄存器上下文和栈保存到其他地方,在切回来的时候,恢复先前保存的寄存器上下文和栈,直接操作栈则基本没有内核切换的开销,可以不加锁的访问全局变量,所以上下文的切换非常快。
区别
进程与线程比较
-
地址空间:线程是进程内的一个执行单元,进程内至少有一个线程,它们共享进程的地址空间,而进程有自己独立的地址空间
-
资源拥有:进程是资源分配和拥有的单位,同一个进程内的线程共享进程的资源
-
线程是处理器调度的基本单位,但进程不是
-
二者均可并发执行
-
每个独立的线程有一个程序运行的入口、顺序执行序列和程序的出口,但是线程不能够独立执行,必须依存在应用程序中,由应用程序提供多个线程执行控制
协程与线程进行比较
-
一个线程可以多个协程,一个进程也可以单独拥有多个协程
-
线程进程都是同步机制,而协程则是异步
-
协程能保留上一次调用时的状态,每次过程重入时,就相当于进入上一次调用的状态