赞
踩
进程:
进程是系统进行资源分配的最小单位,是应用程序运行的载体,可以看作是正在执行的程序,进程的创建,切换,销毁所占用的开销比较大,相对比较稳定安全.
线程:
线程是CPU任务调度和执行的最小单位,一个进程可以包含多个线程,这些线程可以共享同一进程的系统资源,线程之间的通信主要通过共享内存,上下文切换很快,资源开销较少,但是相对于进程不够稳定容易丢失数据.
协程:
协程是一种用户态的轻量级线程,协程的调度完全由用户控制,一个线程可以有多个协程,一个进程也可以单独拥有多个协程,协程拥有自己的寄存器上下文和栈,直接操作栈则基本没有内核切换的开销,可以不加锁的访问全局变量,所以上下文的切换非常快.
使用加号(+)运算符:
这是最简单和最直接的方式,但需要注意的是,每次使用加号拼接字符串时,Go会创建一个新的字符串副本,这可能会导致性能问题,特别是在大量字符串拼接的场景中。
使用fmt.Sprintf函数:
fmt.Sprintf是一个格式化函数,它接受一个格式字符串和一组参数,然后返回一个拼接后的字符串。这种方法适用于需要格式化字符串的场景。
var s1 string = "Hello, "
var s2 string = "World!"
var result string = fmt.Sprintf("%s%s", s1, s2) // 结果为 "Hello, World!"
var builder strings.Builder
builder.WriteString("Hello, ")
builder.WriteString("World!")
result := builder.String() // 结果为 "Hello, World!"
slice := []string{"Hello", "World"}
separator := ", "
result := strings.Join(slice, separator) // 结果为 "Hello, World"
数组是值类型,切片是引用类型
数组的长度是固定的,并且在初始化时长度就已经确定,切片的长度是可变的,可以通过扩容追加元素
数组在函数中传递时会复制一份副本进行传递,在函数中修改数组中的元素不会影响原数组,切片在函数中传递时只会复制len和cap,
底层共用同一个数组,在函数中修改元素的值会影响原切片
数组需要遍历计算数组长度,时间复杂度为O(n)
切片底层包含len字段,可以通过len计算切片长度,时间复杂度为O(1)
数值类型(如:int, float32, float64, complex64, complex128, uint8(即 byte), rune(即 int32))
布尔类型(bool)
字符串类型(string)
数组类型(array)
结构体类型(struct)
切片类型(slice)
映射类型(map)
通道类型(channel)
接口类型(interface)
函数类型(function)
指针类型(pointer)
Beego和Gin是两个在Go语言生态系统中广泛使用的Web框架,它们各有优缺点,适用于不同的开发场景。以下是它们之间的主要区别:
MVC支持:Beego支持完整的MVC(Model-View-Controller)模式,而Gin则不直接支持。这意味着在Gin中,开发者需要自己实现MVC模式。
路由和Session:Beego支持正则路由和Session功能,而Gin则不支持。在Gin中,如果需要实现Session功能,开发者需要安装额外的包,如github.com/astaxie/session。
性能:Gin是一个轻量级的Web框架,以高性能和简洁的设计著称。它使用了快速的HTTP路由器,能够处理大量的并发请求。相比之下,Beego可能在性能方面稍逊一筹。
G:goroutine,Go协程,是参与调度与执行的最小单位,G的数量无限制,理论上只受内存的影响
M:machine,系统级线程,M的数量有限制,默认数量限制是 10000,但是内核很难支持这么多的线程数,所以这个限制可以忽略。可以通过 debug.SetMaxThreads() 方法进行设置,如果有M空闲,那么就会回收或者睡眠。
P:processor,调度器,虚拟处理器,包含了运行goroutine的资源,如果线程想运行goroutine,必须先获取P,P中还包含了可运行的G队列,P的数量受本机的CPU核数影响,可通过环境变量$GOMAXPROCS或在runtime.GOMAXPROCS()来设置,默认为CPU核心数。
M与P的数量没有绝对关系,一个M阻塞,P就会去创建或者切换另一个M,所以,即使P的默认数量是1,也有可能会创建很多个M出来。
GMP调度流程
1.创建并保存G:新创建的G会先保存在P的本地队列中,如果P的本地队列已经满了就会保存在全局的队列中。
2.唤醒或新建 M,绑定 P,用于执行G:G只能运行在M中,一个M必须持有一个P。在创建G时,运行的G会尝试唤醒其他空闲的P和M组合去执行。
3.M 获取 G:M首先从P的本地队列获取 G,如果 P为空,则从全局队列获取 G,如果全局队列也为空,则从另一个本地队列偷取一半数量的 G
4.M调度G执行:如果在执行 G 的过程发生系统调用阻塞(同步),会阻塞G和M(操作系统限制),此时P会和当前M解绑,并寻找新的M,如果没
有空闲的M就会新建一个M ,接着继续执行P中其余的G;如果M在执行G的过程发生网络IO等操作阻塞时(异步),阻塞G,不会阻塞M。M会寻找P中
其它可执行的G继续执行,G会被网络轮询器network poller 接手,当阻塞的G恢复后,G从network poller 被移回到P的 LRQ 中,重新进
入可执行状态。
Go V1.5 三色标记法
三色标记法在不采用STW保护时会出现:
这两种情况同时满足,会出现对象丢失
解决方案:
屏障:
2. 删除屏障:被删除的对象,如果自身为灰色或者白色,那么被标记为灰色(满足弱三色不变式)
不足:回收精度低,一个对象即使被删除了最后一个指向它的指针也依旧可以活过这一轮,在下一轮GC中被清理掉。
Go V1.8的三色标记法+混合写屏障机制
具体操作:
设计模式(design pattern):是对软件设计中普遍存在、反复出现的问题所提出的解决方案,这里的问题就是我们应该怎么去写/设计我们的代码,让我们的代码可读性、可扩展性、可重用性、可靠性更好,通过合理的代码设计让我们的程序拥有“高内聚,低耦合”的特性,这就是设计模式要解决的问题。
本质是为了提高软件的可维护性、可扩展性、通用性,并降低软件的复杂度。
简单工厂
简单工厂就是我们首先声明一个类,这个类叫做工厂类,在这个内我们可以声明一个(静态)方法,这个方法会根据参数的值生成相应的对象(我们把这个对象叫“产品”)。
简单工厂的好处:
package main import "fmt" // 对象接口 type BMW interface { run() } // 构建对象1 type BMW730 struct { } func (b BMW730) run() { fmt.Println("BMW730 is running...") } // 构建对象2 type BMW840 struct { } func (b BMW840) run() { fmt.Println("BMW840 is running...") } // 工厂对象 type Factory struct { } func (f Factory)produceBMW(BMW_TYPE string) BMW { switch BMW_TYPE { case "BMW730": return BMW730{} case "BMW840": return BMW840{} default: return nil } } func main() { // 生成工厂对象 factory := new(Factory) // 使用工厂对象生成产品对象 p1 := factory.produceBMW("BMW730") p1.run() p2 := factory.produceBMW("BMW840") p2.run() }
package main import "fmt" type Subject interface { ProxyFun() string } // 声明代理类 type Proxy struct { real RealSubject } func (p Proxy) ProxyFun() string { var ans string // 在调用真实对象之前,检查缓存、判断权限等 p.real.PreFun() p.real.RealFun() p.real.AfterFun() // 在调用完操作之后,可以缓存结果、对结果进行处理等(如脱敏)、记录日志等 return ans } type RealSubject struct { } func (s RealSubject) RealFun() { fmt.Println("real...") } func (s RealSubject) PreFun() { fmt.Println("Pre...") } func (s RealSubject) AfterFun() { fmt.Println("After...") } func main() { rs := RealSubject{} proxy := Proxy{real: rs} proxy.ProxyFun() }
package main import "fmt" // 发布者-主题 type Subject struct { observers []Observer content string } func NewSubject() *Subject { return &Subject{ observers: make([]Observer, 0), } } // 添加订阅者 func (s *Subject) AddObserver(o Observer) { s.observers = append(s.observers, o) } // 通知消费者 func (s *Subject) Notify() { for _, o := range s.observers { o.SendMessage(s) } } // 发布消息 func (s *Subject) UpdateContent(content string) { s.content = content s.Notify() } // 观察者-订阅者接口 type Observer interface { SendMessage(*Subject) } // 订阅者 type Reader struct { name string } func NewReader(name string) *Reader { return &Reader{ name: name, } } func (r Reader) SendMessage(s *Subject) { fmt.Println(r.name + " " + s.content) } func main() { subject := NewSubject() reader1 := NewReader("qiliang") reader2 := NewReader("xiaolin") subject.AddObserver(reader1) subject.AddObserver(reader2) subject.UpdateContent("ni hao !") }
Go中没有类的概念,只能使用结构体来模拟类,并且go中的结构体只能有属性不能定义方法,结构体的所有字段在内存中是连续分布的.
// 如果结构体的名称是大写,表示该结构体可以被其他包访问
// 反之不能被访问
type Person struct {
// 如果字段的名称是大写,表示该字段可以被其他包访问
// 反之不能访问
Name string
age int
}
面向对象的三大特性: 继承、封装、多态
Golang仍然有面向对象编程的特性,只是实现的方式和其他OOP语言不一样
封装
实现封装的步骤:
继承
继承可以解决代码复用,让编程更加靠,近人类思维,当多个结构体存在相同属性和方法时,可以从这些结构体中抽象出结构体,在该结构体中定义属性和方法,其他结构体不需要重新定义这些属性和方法,也就是说在Go中是通过组合的方式实现继承的
多态
Golang中的多态是通过接口实现的,由于Golang中是依靠组合实现继承的,所以不属于多态
type 接口名 interface{
方法名(参数列表) 返回值列表
方法名(参数列表)返回值列表
}
1、含义不同
函数function是一段具有独立功能的代码,可以被反复多次调用,从而实现代码复用。而方法method是一个类的行为功能,只有该类的对象才能调用。
2、方法有接受者,而函数无接受者
Go语言的方法method是一种作用于特定类型变量的函数,这种特定类型变量叫做Receiver(接受者、接收者、接收器);
接受者的概念类似于传统面向对象语言中的this或self关键字;
Go语言的接受者强调了方法具有作用对象,而函数没有作用对象;
一个方法就是一个包含了接受者的函数;
Go语言中, 接受者的类型可以是任何类型,不仅仅是结构体, 也可以是struct类型外的其他任何类型。
3、函数不可以重名,而方法可以重名
只要接受者不同,则方法名可以一样。
4、调用方式不一样
方法是对象通过.点号+名称来调用,而函数是直接使用名称来调用。
方法的调用需要指定类型变量调用,函数则不需要
注:方法和函数的访问权限都受大小写影响,小写本包,大写全局
5、方法需要指定所属类型,可以是结构体也可以是自定义type,函数则通用
6、函数的形参与传参类型需要一致,方法可以改变
func (student *Student) ShowInfo(形参) 返回值 {
student.Name = "张三"
student.Age = 18
fmt.Printf("name = %v, age = %v", student.Name, student.Age)
}
func (student Student) ShowInfo(形参) 返回值 {
student.Name = "张三"
student.Age = 18
fmt.Printf("name = %v, age = %v", student.Name, student.Age)
}
func test(i *int) {
}
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。