[Go] go基础4

news/2024/11/28 4:27:01/

1. 并发编程

1.1 并发和并行

并发: 多个线程在同个核心的CPU上运行.并发的本质是串行.

并行: 多个线程在多个核心的CPU上运行.

1.2 协程和线程

协程: 独立的栈空间,共享堆空间,调度由用户控制,本质上有点类似用户及线程,这些用户及线程的调度也是自己实现的.

线程: 一个线程上可以跑多个协程,协程是轻量级的线程.(操作系统调度的)

1.3 goroutine

Go 语言中goroutine就是一种机制,类似于线程,但它是由Go运行时(runtime)调度和管理
Go程序会智能地将goroutine中的任务合理分配给每个CPU
Go语言之所以被称为现代化的编程语言,就是因为它在语言层面已经内置了调度和上下文切换机制.
在Go语言编程中你不需要自己去写进程,线程,协程,你的技能包里只要有一个goroutine就可以.
当你需要让某个任务并发执行时,只需要把这个任务包装成一个函数.
开启一个goroutine去执行这个函数就可以了,就是这么简单粗暴.

1.4 协程的使用

正常情况下

func main() {test()
}func test() {for i := 0; i < 10; i++ {fmt.Println(i)}
}
结果
0
1
2
3
4
5
6
7
8
9

开启协程方法1:

go 方法名()

go test()

可以看到这里main和test是一起打印的.

func main() {go test()for i := 0; i < 10; i++ {fmt.Println("main", i)time.Sleep(time.Microsecond * 100)}time.Sleep(time.Second)fmt.Println("done")
}func test() {for i := 0; i < 10; i++ {fmt.Println("test", i)time.Sleep(time.Microsecond * 100)}
}
结构:
main 0
test 0
test 1
main 1
main 2
test 2
main 3
test 3
main 4
test 4
main 5
test 5
test 6
main 6
main 7
test 7
test 8
main 8
main 9
test 9
done

1.4.2 sync.WaitGroup

线程开启时候协程
goroutine 开启wait.add(1) 计数器加1
goroutine结束wait.Done()计数器减1
groutine退出wait.wait()判断当前grouproutine是否为0,为0就退出.

// 1. 定义计数器
var wait sync.WaitGroupfunc main() {// 2.开启一个协程计算器+1wait.Add(1)go test()// 4.计算器为0时退出wait.Wait()fmt.Println("Done!")
}
func test() {for i := 0; i < 10; i++ {fmt.Println("main", i)time.Sleep(time.Microsecond * 100)}// 3.协程执行完毕,计数器-1wait.Done()
}

2. channel

2.1 Channel说明

  • 共享内存交互数据弊端

    • 单纯地将函数并发执行是没有意义的,函数与函数间需要交换数据才能体现并执行函数的意义.
    • 虽然可以使用共享内存进行数据交互,但是共享内存在不同的goroutine中容易发生竞态问题.
    • 为了保证数据交换的正确性,必须使用互斥量对内存进行加锁,这种做法势必造成性能问题.
  • channel好处

    • Go语言中的通道(channel)是一种特殊的类型.
    • 通道像一个传送带或者队列,总是遵循先进先出规则,保证收发数据的顺序.
    • 每一个通道都是一个具体类型的管道,也就是声明channel的时候需要为其指定元素类型.
    • goroutine并发执行时,channel就是他们之间的连接
    • channel是让一个goroutine发送特定的值到另一个goroutine的通讯机制

2.2 channel类型

var 变量 chan 元素类型

var ch1 chan int //整形管道
var ch2 chan bool //布尔型
var cha3 chan []int //切片管道

func main() {// 1. 定义channel// make 可以给切片,map,channel分配内存// chan 关键字,int channel类型,5 channel的长度大小,就是最多可以往ch1里存多少个数据,如果存第6个就会出错.ch1 := make(chan int, 5)// 2. 向channel存入数据ch1 <- 10// 3. 从channel取数据v1 := <-ch1fmt.Println("v1", v1)// 4. 空channel且没有关闭 取值会报错
}结果:v1 10

2.3 channel 循环取值

func main() {ch1 := make(chan int, 5)ch1 <- 1ch1 <- 2ch1 <- 3ch1 <- 4ch1 <- 5close(ch1)for i := range ch1 {fmt.Println(i)}
}
结果:
1
2
3
4
5

如果没有close就会报错

fatal error: all goroutines are asleep - deadlock!

2.4 select 多路复用

  1. select说明

    • 传统的方法遍历管道时,如果不关闭会阻塞而导致deadlock,在实际开发中,我们不能确定具体什么时间该关闭管道.
    • 这种方式可以实现从多个管道接收值的需求,但运行性能会差很多
    • 为了应对这种场景,Go内置了select关键字,可以同时响应多个管道的操作.
    • select使用类似switch语句,他有一系列case分支和一个默认的分支.
    • 每个case会对应一个管道的通信(接收和发送)过程.
    • select会一直等待,直到某个case的通信操作完成时,就会执行case分支对应的语句.
    func main() {// 1. 定义channelch1 := make(chan int, 10)for i := 0; i < 10; i++ {ch1 <- i}ch2 := make(chan string, 10)for i := 0; i < 10; i++ {ch2 <- strconv.Itoa(i)}for {select {case v := <-ch1:fmt.Println("int channel", v)time.Sleep(100 * time.Millisecond)case s := <-ch2:fmt.Println("string channel", s)time.Sleep(100 * time.Millisecond)default:fmt.Println("Channel 中数据已经取完.")return}}
    }
    结果
    int channel 0
    string channel 0
    int channel 1
    string channel 1
    int channel 2
    string channel 2
    int channel 3
    string channel 3
    int channel 4
    string channel 4
    int channel 5
    int channel 6
    int channel 7
    string channel 5
    string channel 6
    int channel 8
    int channel 9
    string channel 7
    string channel 8
    string channel 9
    Channel 中数据已经取完.
    

    3. 互斥锁

    • 互斥锁是一种常用的控制共享资源访问的方法,它能够保证同时只有一个goroutine可以访问共享资源.
    • Go语言中使用sync包的Mutex类型来实现互斥锁
    var x intfunc main() {fmt.Println(x)add()fmt.Println(x)
    }
    func add() {for i := 1; i <= 5000; i++ {x += 1}
    }
    结果
    0
    5000
    

    当开启了多个协程对一个资源进行操作,就出现了资源竞争.

    var x int
    var wg sync.WaitGroupfunc main() {wg.Add(2)fmt.Println(x)go add()go add()wg.Wait()fmt.Println(x)
    }
    func add() {for i := 1; i <= 5000; i++ {x += 1}wg.Done()
    }
    结果
    0
    7606
    第二次运行结果是
    0   
    8813
    

    为了保证数据正常,需要加上互斥锁.

    var x int
    var wg sync.WaitGroup// 1. 定义互斥锁
    var lock sync.Mutexfunc main() {wg.Add(2)fmt.Println(x)go add()go add()wg.Wait()fmt.Println(x)
    }
    func add() {for i := 1; i <= 5000; i++ {// 2. 执行前加锁lock.Lock()x += 1// 3. 执行完解锁lock.Unlock()}wg.Done()
    }
    结果
    0
    10000
    

3. fmt

常用占位符

参数功能
%v按值的本来值输出
%+v在%v基础上,对结构体字段名和值进行展开
%#v输出go语言语法格式的值
%T类型的值
%%输出%%本体
%b以二进制显示
%o以8进制显示
%d以10进制显示
%x以16进制显示
%X以16进制显示,字母大写
%UUnicode字符
%f浮点数
%p指针,16进制方式显示

3.1 Sprint

将格式化的数据复制给其他变量

s := fmt.Sprintf("姓名: %s age: %d","张三",24)
fmt.Println(s)
结果
姓名: 张三 age: 24

fmt.Printf 不换行
fmt.Println 换行

4. 时间

4.1 时间转换

  • 时间对象, golang中定义的一个对象
    • time.Now()
  • 时间戳: 秒整数形式,1970年1月1日开始
    • now.Unix()
  • 格式化时间:人看
    • now.Format(“2006-01-02 15:04:05”)
func main() {// 1. 获取时间对象now := time.Now()fmt.Printf("%T %v\n", now, now)// 2. 格式化时间 将时间对象,转换为格式化的时间strTime := now.Format("2006-01-02 15:04:05")fmt.Printf("%T %v\n", strTime, strTime)// 3. 时间戳格式 秒的整数形式ts := now.Unix()fmt.Printf("%T %v\n", ts, ts)// 4. 格式化时间转换成时间对象// 4.1 设置时区loc, _ := time.LoadLocation("Asia/Shanghai")// 4.2 传入时间标记2006-01-02 15:04:05 这个值是不能修改的timeObj, _ := time.ParseInLocation("2006-01-02 15:04:05", strTime, loc)fmt.Println(timeObj.Unix())
}
结果
time.Time 2022-12-05 14:00:40.2687082 +0800 CST m=+0.004188601
string 2022-12-05 14:00:40
int64 1670220040          
1670220040   

4.2 时间类型

func main() {now := time.Now()year := now.Year()month := now.Month()day := now.Day()hour := now.Hour()minute := now.Minute()second := now.Second()fmt.Printf("%v %v %v %v %v %v", year, month, day, hour, minute, second)
}// %02d 保留2位十进制数字,不够就高位补0Today := fmt.Sprintf("%02d-%d-%02 %d:%d:%d", year, month, day, hour, minute, second)fmt.Printf("%T %v", Today, Today)
结果
2022 December 5 14 10 53
2022-12-05 14:15:11
string

4.3 时间间隔

参数含义
nanosecond纳秒 , 十亿分之一秒
Microsecond1000*nanosecond微秒,一百万分之一秒
Millisecond1000*Microsecond毫秒,千分之一秒
Second1000*Microsecond,秒
Minute60*second,分
Hour60*Minute,小时

4.3.1 Add方法

func main() {now := time.Now()fmt.Println("现在是:", now)m, _ := time.ParseDuration("-1m")m1 := now.Add(m)fmt.Println("前1分钟是:", m1)
}
结果
现在是: 2022-12-05 14:42:46.8769096 +0800 CST m=+0.003688201
前1分钟是: 2022-12-05 14:41:46.8769096 +0800 CST m=-59.996311799

5. Flag

Go语言内置的flag包实现了命令行参数的解析,flag包使得开发命令行工具更为简单.

func main() {// 1. String variablesvar name stringvar address stringflag.StringVar(&name, "name", "张三", "姓名")flag.StringVar(&address, "address", "上海", "地址")flag.Parse()fmt.Println(flag.Args())
}在命令行下执行--help
PS D:\golang\day3\03.flag> go run main.go --help
Usage of C:\Users\Q\AppData\Local\Temp\go-build1568490171\b001\exe\main.exe:-address string地址 (default "上海")-name string姓名 (default "张三")

命令行传参

&name 变量的指针,传入的数据赋值给他

name 命令行里的key

“张三” 如果不传递张三就作为默认值

“姓名” --help里的提示信息.

func main() {// 1. String variablesvar name stringvar address stringflag.StringVar(&name, "name", "张三", "姓名")flag.StringVar(&address, "address", "上海", "地址")flag.Parse()fmt.Println(name, address)
}
命令行执行,如果不传值,就会用默认值替代
PS D:\golang\day3\03.flag> go  run main.go -name "李四" -address "北京"
李四 北京
PS D:\golang\day3\03.flag> go  run main.go -name "李四"                
李四 上海
	fmt.Println(name, address)// Args 可以接收除了name和address以外的传入变量fmt.Println(flag.Args())
结果
PS D:\golang\day3\03.flag> go  run .\main.go  1 3 2 4
张三 上海
[1 3 2 4]

6. net-http

它既能提供server端,又能提供client端.

6.1 Get请求

方法名称描述
Header()用户设置或获取响应头信息
write()用于写入数据响应体
WriteHeader()用于设置响应状态码,若不调用则默认状态码为200 OK.

6.1.1 返回数据

启动一个http服务,进行简单的返回一个数据

/*
1. 路由
2. 处理函数1. 解析请求数据2. 处理函数将结果进行返回3. 启动服务
*/
func main() {// 1. 定义路由http.HandleFunc("/req/get", dealGetHandler)fmt.Println("http://127.0.0.1:8080/req/get")// 3. 启动服务// addr: 当前server监听的端口号,handler:处理函数http.ListenAndServe(":8080", nil)}// 2. 定义处理函数,用驼峰命名,以xxxHandler为函数名
// Get 请求
// http.ResponseWriter 返回数据给浏览器的,本质是一个interface接口,定义了三个方法,进行返回数据
// *http.Request 将传过来的参数放入Request结构体中. 解析url中的数据或post请求body的数据
func dealGetHandler(w http.ResponseWriter, r *http.Request) {// 直接返回数据w.Write([]byte("hello world"))
}

6.1.2 解析数据

	// 1. 解析请求的数据query := r.URL.Query()fmt.Println(query)
当浏览器输入http://127.0.0.1:8080/req/get?name=zhangsan时,得到以下返回
http://127.0.0.1:8080/req/get?name=zhangsan

请添加图片描述

请添加图片描述

6.1.3 通过get取值

func deal2GetHandler(w http.ResponseWriter, r *http.Request) {query := r.URL.Query()name2 := query.Get("name")fmt.Println(name2)// 直接返回数据w.Write([]byte("hello world"))
}

6.1.4 返回json值

func deal2GetHandler(w http.ResponseWriter, r *http.Request) {query := r.URL.Query()name2 := query.Get("name")fmt.Println(name2)// 1.1 直接返回字符串//w.Write([]byte("hello world"))// 1.2 返回jsontype Info struct {Name     stringPassword stringAge      int64}u := Info{Name:     name2,Password: "123456",Age:      18,}json.NewEncoder(w).Encode(u)
}
返回内容:
{"Name":"张三","Password":"123456","Age":18}

请添加图片描述

6.2 Post请求

func main() {// 1. 定义路由http.HandleFunc("/req/get", deal2GetHandler)http.HandleFunc("/req/post", dealPostHandler)fmt.Println("http://127.0.0.1:8080/req/get")// 3. 启动服务// addr: 当前server监听的端口号,handler:处理函数http.ListenAndServe(":8080", nil)
}
// Get部分省略// 和Get请求一样写法
func dealPostHandler(w http.ResponseWriter, r *http.Request) {// r.URL.query() 从url取参数// post 从http的body取中获取数据bodyContent, _ := ioutil.ReadAll(r.Body)fmt.Printf("%T %v\n", bodyContent, bodyContent)w.Write([]byte("hello Post"))
}

请添加图片描述

6.2.1 解析POST传入的值

  1. 通过iouttil.ReadAll 读出http.Request.body的结构体
  2. 通过json的绑定,将数据绑定到定义的结构体
func dealPostHandler(w http.ResponseWriter, r *http.Request) {// r.URL.query() 从url取参数// post 从http的body取中获取数据bodyContent, _ := ioutil.ReadAll(r.Body)// uint8 转string//strData := string(bodyContent)// string 转结构体// 定义个一个格式一样的structvar d Info2json.Unmarshal(bodyContent, &d)// 获取到的name的数据fmt.Println(d.Name, d.Password)//fmt.Printf("%T %v\n", bodyContent, strData)w.Write([]byte("hello Post"))
}type Info2 struct {Name     string `json:"name"`Password string `json:"password"`
}
结果
zhangsan root123

请添加图片描述

6.3 请求数据

6.3.1 Get方法

  1. 通过body进行解析
func main() {apiUrl := fmt.Sprintf("http://127.0.0.1:8080/req/get?name=zhangsan")resp, err := http.Get(apiUrl)if err != nil {fmt.Println(err)return}body, _ := ioutil.ReadAll(resp.Body)fmt.Println(string(body))var b Infojson.Unmarshal(body, &b)fmt.Println(b.Name)
}type Info struct {Name string `json:"name"`
}
结果
{"Name":"zhangsan","Password":"123456","Age":18}zhangsan
  1. 通过url进行解析
func main() {//从这里开始apiUrl := "http://127.0.0.1:8080/req/get"data := url.Values{}data.Set("name", "zhangsan1")u, _ := url.ParseRequestURI(apiUrl)u.RawQuery = data.Encode()// 到这里其实就是在拼接url// 后面的和之前的一样resp, err := http.Get(u.String())if err != nil {panic(err)}body, _ := ioutil.ReadAll(resp.Body)fmt.Println(string(body))var i Infojson.Unmarshal(body, &i)fmt.Println(i.Name, i.Password, i.Age)
}type Info struct {Name     string `json:"name"`Password string `json:"Password"`Age      int    `json:"Age"`
}
结果:
{"Name":"zhangsan1","Password":"123456","Age":18}zhangsan1 123456 18

6.3.2 Post方法

func main() {url := "http://127.0.0.1:8080/req/post"// 表单数据提交 form submission//contentType := "application/x-www-form-urlencoded"// Json数据提交contentType := "application/json"data := `{"name": "zhangsan","password": "root123"
}`resp, _ := http.Post(url, contentType, strings.NewReader(data))b, _ := ioutil.ReadAll(resp.Body)fmt.Println(string(b))
}
结果:
hello Post
服务器端结果:
zhangsan root123

7. Os模块

func main() {// 1. 获取当前目录fmt.Println(os.Getwd())// 2. 切换路径os.Chdir("d:\\game\\")fmt.Println(os.Getwd())// 3. 创建文件夹os.Mkdir("test", 0777)// 4. 删除//os.Remove("test")// 5. 重命名os.Rename("test", "test2")// 6. 新建文件os.Create("test2/test.txt")
}

http://www.ppmy.cn/news/2604.html

相关文章

CRC 循环冗余检验【计网必考】

CRC 循环冗余检验作为一个重点&#xff0c;也是数据链路层必考的一个考点&#xff0c;所以我把差错检测单独拿出来分析一起看一下。总结不易&#xff0c;一个简单的攒&#xff0c;Thanks♪(&#xff65;ω&#xff65;)&#xff89; 目录 一、介绍及工作原理 二、校验计算过程…

浅浅的分析Spring底层事务原理

Spring事务底层原理一、EnableTransactionManagement工作原理二、Spring事务基本执行原理三、Spring事务的过程四、Spring事务传播机制五、Spring事务传播机制分类&#xff08;1&#xff09;案例分析、情况1&#xff08;2&#xff09;案例分析、情况2&#xff08;3&#xff09;…

c++11中的declval和decltype

一、declval的介绍 std::declval定义在头文件中&#xff1a; template<class T> typename std::add_rvalue_reference<T>::type declval() noexcept;看定义它应该是返回一个右值引用(在T 是&#xff08;可有 cv 限定的&#xff09; void &#xff0c;此情况下返回…

Maya多边形物体批量材质传递工具v1.0发布及教程

一、插件介绍&#xff1a; 在大量场景制作时&#xff0c;当前期模型和材质没有同时完成&#xff0c;而用白模进行场景搭建后&#xff0c;能否后期&#xff0c;快速根据相同模型结构&#xff0c;快速识别物体并批量赋材质吗&#xff1f;答案是现在可以了。下面介绍的就是解决此…

ISO9001有什么好处

1、帮助大中小企业间公平竞争 有些中小企业不善于在国际市场中推销自己&#xff0c;当面对艰难竞争时&#xff0c;标准证书可以为其说话&#xff0c;给企业带来信誉。 2、帮助企业打开出口市场 从事标准化工作取得的收益比很多开展小型业务人员原本认为的要大。标准既重要且有趣…

springboot解决jsp页面和control互相跳转

Jsp页面和control交互是前后端交互最常见的模式。 1、首先配置Jsp。 配置Jsp 第一步保证自己的项目是web。打开目录设置。目录设置是idea中最常打开的窗口。 选择模块&#xff0c;配置web选择webapp路径。 然后就可以新建jsp 2、另外control建立及其简单。 新建软件包&am…

来用Service worker吧

之前说了笔者写了一个微前端框架。在微前端中子应用切换前要先获取到子应用的资源&#xff08;比如js、css&#xff09;和 html 片段进行加载。那么这里就涉及到一个“老生常谈”的话题&#xff1a;资源缓存。 为了缓存子应用的 js、css资源&#xff0c;笔者分两步进行&#x…

GCN:分布式训练大规模深度图神经网络

图(Graph)数据在现实世界中非常常见,例如社交网络、交通网络、物理系统等等,近几年图神经网络的发展将图数据的分析与深度神经网络结合,在越来越多的领域发挥出重要的作用,例如电商推荐、生物化学结构分析、反恐反诈风险控制等等。数据规模也呈现越来越大之势,动辄上千万…