Go项目实战|青训营笔记

114 阅读3分钟

这是我参与「第三届青训营 -后端场」笔记创作活动的的第2篇笔记

Go为什么并发更快?

  1. 并发和并行的区别

并行:多线程程序在多个核的cpu上运行

并发:多线程程序在一个核的CPU上运行

GO可以最大限度的发挥多核计算机的优势---为并发而生的语言

  1. 协程 goroutine

对比线程,理解协程

qaq 原来线程是内核态,不过之前好晕呀,这个图还不错

协程:用户态,轻量级线程,栈MB级别

线程:内核态,线程跑多个协程,栈KB级别

举例:快速打印hello

func hello(i int){
  println("hello")
}

func main(){
  for i:=0;i<5;i++{
    go func(j int){
      hello(j)
    }(i)
  }
  // 暴力,防止父进程在子进程之前结束
  time.Sleep(time.Second)
}

1.2 协程之间的通信CSP(communication Sequential Processes)

通过共享内存来进行通信,会存在一个race condition的问题

提问?

  1. 难道通过channel就不会有race condition?

不应该呀。go 不是会通过-race 来检测是否有race condition的存在吗

回答:在go中就算利用channel也会存在race condition?

那么新的疑问产生,go的channel优势在哪里呢?

毕竟go的宗旨是:

do not communicate by sharing memroy; instead, share memory by communication

  1. 利用通道通信和利用共享内存通信的优缺点在哪里呢?

communicate by sharing memory: this is the "traditional" way to deal with multithreading. If some data is shared by two threads, to keep them from for example attempting to write both at the same time, some synchronization primitives must be used. This is notoriously difficult to debug. It is interesting to keep in mind that in time sharing, the processor is allocating a time slice (for example 100ms) to one thread, and then switches context, and allocates the next time slice to another thread. The switch happens while the operating system has no knowledge of what the code run by the threads is doing. If operations aren't atomic, they can be interrupted by the operating system at any step.

share memory by communicating: Typically, this is the case using an actor model where each actor is mapped to a processor core. Actors exchange messages using a mailbox system where the receiver receives a copy of the message. There is no shared data, so no need for synchronization mechanisms like mutexes. Another name for this architecture is "shared nothing".

1.3 channel

make(chan 元素)

  • 无缓冲通道 make( chan int)
  • 有缓冲通道 make(chan int ,2)

使用无缓冲通道,需要发、送同步,所以又叫做同步通道

因此,但有的时候来不及,会导致堵塞,这时候,就可以用有缓冲问题,来解决同步问题

举例

A 子协程发送0-9数字

B 子协程计算输入数字的平方

主协程输出最后的平方数

要求 同步(顺序)

package main

import "fmt"

func main() {
	a := make(chan int)
	b := make(chan int)
	go func() {
		defer close(a)
		for i := 0; i < 10; i++ {
			a <- i
		}
	}()
	go func() {
		defer close(b)
		for i := range a {
			b <- i * i
		}
	}()
	for i := range b {
		fmt.Println(i)
	}
}

1.4 并发安全LOCK

对变量进行2000次+1操作,采用五个协程并发执行

package main

import (
	"fmt"
	"sync"
	"time"
)

var (
	x    int64
	lock sync.Mutex
)

func main() {
	x = 0
	for i := 0; i < 5; i++ {
		go addUnlock()
	}
	time.Sleep(time.Second)
	fmt.Println(x)
  // 7895
	x = 0
	for i := 0; i < 5; i++ {
		go addLock()
	}
	time.Sleep(time.Second)
	fmt.Println(x)
  // 10000
}

func addUnlock() {
	for i := 0; i < 2000; i++ {
		x++
	}
}
func addLock() {
	for i := 0; i < 2000; i++ {
		lock.Lock()
		x++
		lock.Unlock()
	}
}

避免对共享内存进行非并发操作

1.5 waitgroup

来实现main 和协程任务的同步

waitroup

暴露三个接口

  1. add->计数器+1
  2. done->计数器-1
  3. wait->阻塞直到计数器=0

计数器:

开启协程+1,执行结束-1,主协程堵塞直到计数器=0

总结

  1. goroutine
  2. channel
  3. sync
  • lock(mutex)
  • waitgroup

二、依赖管理

go path->go vender->go module

2.1 GO PATH

2.1.1 go path

  • 🔲环境变量

输入 cd $GOPATH

输入 ls 或者输入 tree -L 1

bin pkg src

( .

|___bin 项目编译的二进制文件

|___pkg 项目编译的中间产物,用来加速编译

|___src 项目源码

)

√ 项目代码直接依赖src下的代码

√ go get 下载最新版本的包到src目录下

2.1.1.1 存在问题

没办法实现多版本的控制

2.1.2 go vender

每个项目都引入一个版本,从而解决依赖冲突的问题

但是如果一个项目,引入不兼容的版本,那么久导致报错

2.1.3 go module

解决了很多问题

  • 通过go mod 进行管理依赖包版本
  • 通过go get/go mod指令工具管理依赖包

2.2 依赖管理三要素

  1. 配置文件、描述依赖 go.mod
  2. 中心仓库管理依赖库 proxy
  3. 本地工具 go get/mod

2.3 依赖三要素详解

2.3.1 依赖配置----go.mod

module example/project/app  依赖管理基本单元

go 1.16 原生库

 依赖
require(
  example/lib1 v1.0.2    单元依赖
  example/lib1 v1.0.0    // indirect 非直接依赖
)

三个部分组成

关于version的定义---versio(go mod版本规则)

  • 语义版本
  • 基于commit 伪版本

2.3.2 proxy 牛叉

goproxy.cn/ 详细使用参考这个网址

2.3.3 工具 go get/mod

\

不太理解~

// 需要将常用的指令和内容记录下来~

三、测试

  1. 单元测试
  2. mock测试
  3. 基准测试

从上到下,成本增加。从上到下,覆盖率增加

// 不想学习这一张,不过为了以后,还是好好学习吧

四、项目实战

4.1 实现每个话题和对应的评论

4.2 需求分析

用户:需要浏览topic 和postList

topicpage: topic 和 postlist 连个实体

4.3 实体的属性

topic 和 postList是一对多的模型

struct{
  id
  title
  content
  creat_time
}
struct{
  id
  topid_id
  content
  create_time
}

4.4 分层结构

\

√ 数据层:数据model,外部数据的增删改查

√ 逻辑层:业务entity,处理核心业务的逻辑输出

√ 视图层:视图view,处理和外部交互的逻辑

补图

4.5 组件工具

√ Gin高性能go web框架

github.com/gin_gonic/g…

√ go mod

go mod init __(名称)

go get gopkg.in/gin-gonic/gin.v1@1.3.0

4.6 repository-index

在业务中,需要根据id 查找topic,如何实现呢?

利用map

// 老师讲的太粗糙了,课后需要看代码

Q&A:

  1. 线程、进程、协程的通讯方式?

主要关注协程啦。可以通过共享内存和channel来进行通信

对于java通信,是通过mutex的结构来进行通信

  1. 协程和用户线程的区别?

协程可以理解为用户线程

  1. 登录什么时候用session+cookie方式?什么时候用token?

session+cookie适用于后台解密 客户端和服务器

token服务器

  1. 消息队列?

用来解耦的功能

  1. go 在用户态灵活控制,而java利用系统调度。所以用go调度灵活性更高

\