前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
社区首页 >专栏 >A Bite of GoLang(下)

A Bite of GoLang(下)

原创
作者头像
盛国存
发布于 2018-05-14 10:10:32
发布于 2018-05-14 10:10:32
1.1K1
举报
文章被收录于专栏:盛国存的专栏盛国存的专栏

8. Goroutine

8.0、Goroutine介绍

协程 Coroutine
轻量级"线程"

上面的两个特征到底是什么意思呢?下面我们通过具体的事例详细的讲述一下,

代码语言:txt
AI代码解释
复制
package main

import "fmt"

func main() {

	for i := 0; i < 10; i ++ {
		func(i int){
			for {
				fmt.Println("Goroutine :" , i)
			}
		}(i)
	}
}

上面这段代码有问题么? 这就是一个从 0 到 10 的调用,但是匿名函数内部没有中止条件,所以会进入一个死循环。要是我们在匿名函数前加上 go 关键字,就不是刚才的意思了,就变成并发执行这个函数。主程序继续向下跑,同时并发开了一个函数,就相当于开了一个线程,当然我们后面会继续介绍,这个叫 协程

代码语言:txt
AI代码解释
复制
package main

import "fmt"

func main() {

	for i := 0; i < 10; i ++ {
		go func(i int){
			for {
				fmt.Println("Goroutine :" , i)
			}
		}(i)
	}
}

我们再执行这段代码,发现什么都没有输出,这又是为什么呢?因为这个 mainfmt.Println 是并发执行的,我们还来不及print结果, main 就执行完成退出了。Go语言一旦main函数退出了,所有的Goroutine就被杀掉了。

当然要是想看到输出结果,main函数可以在最后sleep一下

代码语言:txt
AI代码解释
复制
package main

import (
	"fmt"
	"time"
)

func main() {

	for i := 0; i < 10; i ++ {
		go func(i int){
			for {
				fmt.Println("Goroutine :" , i)
			}
		}(i)
	}
	time.Sleep(time.Millisecond)
}

这时候就有相关的结果输出了。那我们将现在的10改成1000,又会怎样呢?当然还是可以正常输出的,熟悉操作系统的都知道正常的线程几十个上百个是没啥问题的,1000个还是有点难度的,其它语言通常使用异步IO的方式。在Go语言中我们不用管10个、100个、1000个代码还是一样的写法。

非抢占式多任务处理,由协程主动交出控制权

非抢占式多任务 这又是什么意思呢?下面我们用一个例子来解释一下

代码语言:txt
AI代码解释
复制
package main

import (
	"time"
	"fmt"
)

func main() {

	var a [10]int
	for i := 0; i < 10; i ++ {
		go func(i int){
			for {
				a[i] ++
			}
		}(i)
	}
	time.Sleep(time.Millisecond)
	fmt.Println(a)
}

在运行之前,可以想一下会输出什么呢? 什么也没有输出,进入了死循环。

上图是我的活动监视器的截图,因为是4核的机器,几乎全部占满了。退不出的原因是因为Goroutine ai 交不出控制权,没有yield出去,同时main函数也是一个goroutine,因为没人交出控制权,所以下面的sleep永远也不会执行。

那我该如何交出控制权呢?我们可以做一个IO操作可以交出控制权,当然也可以手动交出控制权

代码语言:txt
AI代码解释
复制
package main

import (
	"time"
	"fmt"
	"runtime"
)

func main() {

	var a [10]int
	for i := 0; i < 10; i ++ {
		go func(i int){
			for {
				a[i] ++
				runtime.Gosched()
			}
		}(i)
	}
	time.Sleep(time.Millisecond)
	fmt.Println(a)
}

只需要加上 runtime.Gosched() ,这样大家都主动让出控制权,这时候代码可以正常输出了

代码语言:txt
AI代码解释
复制
[321 986 890 880 831 881 919 904 861 904]

Process finished with exit code 0

如果我们把goroutine的参数 i 去掉会怎样呢?

直接的看语法上没有什么问题,就变成了一个闭包,使用外部的变量 i

代码语言:txt
AI代码解释
复制
package main

import (
	"time"
	"fmt"
	"runtime"
)

func main() {

	var a [10]int
	for i := 0; i < 10; i ++ {
		go func(){
			for {
				a[i] ++
				runtime.Gosched()
			}
		}()
	}
	time.Sleep(time.Millisecond)
	fmt.Println(a)
}

运行之后会出现什么问题呢?

代码语言:txt
AI代码解释
复制
panic: runtime error: index out of range

goroutine 6 [running]:
main.main.func1(0xc42001a0f0, 0xc42001c060)
	/Users/verton/GoLangProject/src/shengguocun.com/goroutine/route.go:15 +0x45
created by main.main
	/Users/verton/GoLangProject/src/shengguocun.com/goroutine/route.go:13 +0x95

Process finished with exit code 2

这里我们通过Go语言的 go run -race xxx.go ,执行分析一下

代码语言:txt
AI代码解释
复制
sheng$ go run -race route.go
==================
WARNING: DATA RACE
Read at 0x00c420092008 by goroutine 6:
  main.main.func1()
      /Users/verton/GoLangProject/src/shengguocun.com/goroutine/route.go:15 +0x54

Previous write at 0x00c420092008 by main goroutine:
  main.main()
      /Users/verton/GoLangProject/src/shengguocun.com/goroutine/route.go:12 +0x11b

Goroutine 6 (running) created at:
  main.main()
      /Users/verton/GoLangProject/src/shengguocun.com/goroutine/route.go:13 +0xf1
==================

这个地址 0x00c420092008 是谁呢,很显然就是 i ,原因是因为在最后跳出来的时候 i 会变成10,里面的 a[i] ++ 就会是a10 ,所以出错的原因就在这。

代码语言:txt
AI代码解释
复制
sheng$ go run -race route.go
==================
WARNING: DATA RACE
Read at 0x00c420092008 by goroutine 6:
  main.main.func1()
      /Users/verton/GoLangProject/src/shengguocun.com/goroutine/route.go:15 +0x54

Previous write at 0x00c420092008 by main goroutine:
  main.main()
      /Users/verton/GoLangProject/src/shengguocun.com/goroutine/route.go:12 +0x11b

Goroutine 6 (running) created at:
  main.main()
      /Users/verton/GoLangProject/src/shengguocun.com/goroutine/route.go:13 +0xf1
==================

上面还剩一个的两个Goroutine读写的问题需要我们后面介绍的Channel来解决。

8.1、Go语言调度器

多个协程可能在一个或多个线程上运行

首先我们先看一张普通函数和协程的对比图

普通函数main函数和work函数都运行在一个线程里面,main函数在等work函数执行完才能执行其他的操作。可以看到普通函数 main 函数和 work 函数是单向的,但是发现协程的 main 和 work 是双向通道的,控制权可以在work也可以在main,不需要像普通函数那样等work函数执行完才交出控制权。协程中main和work可能执行在一个线程中,有可能执行在多个线程中。

上图就是Go语言的协程, 首先下面会有一个调度器,负责调度协程,有些是一个goroutine放在一个线程里面,有的是两个,有的是多个,这些我们都不需要关注。

goroutine定义
  • 任何函数只需要加上go就能送给调度器运行
  • 不需要在定义时区分是否是异步函数
  • 调度器在合适的点进行切换
  • 使用-race来检测数据访问冲突
goroutine可能的切换点
  • I/O 、select
  • channel
  • 等待锁
  • 函数调用(有时)
  • runtime.Gosched()

上述仅是参考,不能保证切换,不能保证在其他的地方不切换

9. Channel

9.0、Channel介绍

Channel

我们可以开很多个goroutine,goroutine和goroutine之间的双向通道就是channel。

首先我们先来介绍一下channel的用法

代码语言:txt
AI代码解释
复制
ch := make(chan int)

和其他类型类似,都是需要先创建声明

代码语言:txt
AI代码解释
复制
package main

import (
	"fmt"
	"time"
)

func main()  {

	ch := make(chan int)
	go func() {
		for  {
			num := <- ch
			fmt.Println(num)
		}
	}()

	ch <- 1
	ch <- 2
	time.Sleep(time.Millisecond)
}

这就是一个简单的channel示例,同时channel是一等公民,可以作为参数也可以作为返回值,那我们就用一个例子来简单的演示一下

代码语言:txt
AI代码解释
复制
package main

import (
	"fmt"
	"time"
)

func work(channels chan int, num int) {
	for ch := range channels {

		fmt.Println("Work ID :", num)
		fmt.Println(ch)
	}
}

func createWork(num int) chan<- int {

	ch := make(chan int)
	go work(ch, num)
	return ch
}


func main()  {

	var channels [10]chan<- int
	for i := 0; i < 10; i ++ {
		channels[i] = createWork(i)
	}

	for i := 0; i < 10; i ++ {
		channels[i] <- 'M' + i
	}
	time.Sleep(time.Millisecond)
}

输出结果为

代码语言:txt
AI代码解释
复制
Work ID : 3
80
Work ID : 0
77
Work ID : 1
78
Work ID : 6
Work ID : 9
83
Work ID : 4
Work ID : 5
82
86
81
Work ID : 8
85
Work ID : 2
79
Work ID : 7
84

结果为什么是乱序的呢?因为 fmt.Println 有I/O操作;上述例子,可以看到channel既可以作参数,也可以作为返回值。

Buffer Channel
代码语言:txt
AI代码解释
复制
ch := make(chan  int)
ch <- 1

我们要是光发送,没有接收是不行的,程序会报错,比如上述代码运行之后

代码语言:txt
AI代码解释
复制
fatal error: all goroutines are asleep - deadlock!

goroutine 1 [chan send]:
main.main()
	/Users/verton/GoLangProject/src/shengguocun.com/channel/channel.go:42 +0x50

我们可以设置一个缓冲区

代码语言:txt
AI代码解释
复制
ch := make(chan  int, 5)
ch <- 1

缓冲区大小设置为5,只要发送不超过5个都不会报错,下面我们来演示一下buffer channel的使用

代码语言:txt
AI代码解释
复制
func main()  {

	channels := make(chan  int, 5)
	go func() {
		for ch := range channels {

			fmt.Println(ch)
		}
	}()
	channels <- 1
	channels <- 2
	channels <- 3
	channels <- 4
	channels <- 5
	time.Sleep(time.Millisecond)
}

结果输出正常

代码语言:txt
AI代码解释
复制
1
2
3
4
5

Process finished with exit code 0

比如我们确定数据结束了,可以在最后进行close;同时只能是发送方close的

代码语言:txt
AI代码解释
复制
func main()  {

	channels := make(chan  int, 5)
	go func() {
		for ch := range channels {

			fmt.Println(ch)
		}
	}()
	channels <- 1
	channels <- 2
	channels <- 3
	channels <- 4
	channels <- 5
	close(channels)
	time.Sleep(time.Millisecond)
}

直观地从输出结果来看,加不加close这两者是没有区别的。

9.1、使用Channel等待任务结束

前面的例子中我们等待任务结束是通过sleep来处理,因为打印的数据较少,1 毫秒足够;但是这种方式等待任务结束显然不是很优雅。

对于任务结束首先我们需要确定的通知外面我们打印结束了,那我们又如何通知呢?在Go语言中我们不要通过共享内存来通信,而是要通过通信来共享内存。直接用Channel就可以,下面我们来改造上面的例子

代码语言:txt
AI代码解释
复制
package main

import (
	"fmt"
)

type worker struct {
	in chan int
	done chan bool
}

func work(in chan int, done chan bool, num int) {
	for ch := range in {

		fmt.Println("Work ID :", num)
		fmt.Println(ch)
		done<- true
	}
}

func createWork(num int) worker {

	ch := worker{
		in: make(chan int),
		done: make(chan  bool),
	}
	go work(ch.in, ch.done, num)
	return ch
}

func main() {

	var workers [10]worker
	for i := 0; i < 10; i ++ {
		workers[i] = createWork(i)
	}

	for i := 0; i < 10; i ++ {
		workers[i].in <- 'M' + i
		<-workers[i].done
	}
}

打印输出结果

代码语言:txt
AI代码解释
复制
Work ID : 0
77
Work ID : 1
78
Work ID : 2
79
Work ID : 3
80
Work ID : 4
81
Work ID : 5
82
Work ID : 6
83
Work ID : 7
84
Work ID : 8
85
Work ID : 9
86

虽然sleep部分的代码已经删除了,但是发现是顺序打印的,这显然不是我想要的结果。Go语言对等待多任务完成提供了一个库 WaitGroup,下面我们就用它继续重构上述的代码

代码语言:txt
AI代码解释
复制
package main

import (
	"fmt"
	"sync"
)

type worker struct {
	in chan int
	done func()
}

func work(worker worker, num int) {
	for ch := range worker.in {

		fmt.Println("Work ID :", num)
		fmt.Println(ch)
		worker.done()
	}
}

func createWork(num int, wg *sync.WaitGroup) worker {

	worker := worker{
		in: make(chan int),
		done: func() {
			wg.Done() // 每个任务做完了就调用Done
		},
	}
	go work(worker, num)
	return worker
}


func main() {

	var wg sync.WaitGroup
	var workers [10]worker
	for i := 0; i < 10; i ++ {
		workers[i] = createWork(i, &wg)
	}

	wg.Add(10) // Add 总共有多少个任务

	for i := 0; i < 10; i ++ {
		workers[i].in <- 'M' + i
	}

	wg.Wait() // 等待所有的任务做完
}

结果输出

代码语言:txt
AI代码解释
复制
Work ID : 4
81
Work ID : 5
82
Work ID : 1
78
Work ID : 2
79
Work ID : 6
Work ID : 3
80
Work ID : 0
Work ID : 9
86
83
77
Work ID : 7
84
Work ID : 8
85

Process finished with exit code 0

这样相应的结果才是我们想要的。

面试题实战
代码语言:txt
AI代码解释
复制
协程交替执行,使其能顺序输出1-20的自然数

这个问题就不做演示了,留给读者自行发挥。

9.2、用select进行调度

1、select使用

首先我们先来介绍一下select常规的应用场景,比如

代码语言:txt
AI代码解释
复制
var ch1, ch2 chan int 

我们有两个channel,我们想从 ch1、ch2 里面收数据,

代码语言:txt
AI代码解释
复制
var ch1, ch2 chan int
data1 := <- ch1
data2 := <- ch2

谁快我就要谁,这就是我们的select

代码语言:txt
AI代码解释
复制
package main

import (
	"fmt"
)

func main() {

	var ch1, ch2 chan int
	select {

		case data := <- ch1:
			fmt.Println("CH1 的数据:", data)
		case data := <-ch2:
			fmt.Println("CH2 的数据:", data)
		default:
			fmt.Println("没收到 CH1、CH2 的数据")
	}
}

这就相当于做了一个非阻塞式的获取。下面我们就结合一个channel生成器来做一个例子演示

代码语言:txt
AI代码解释
复制
package main

import (
	"fmt"
	"time"
	"math/rand"
)

func genChan() chan int {

	out := make(chan int)
	go func() {

		i := 0
		for {
			time.Sleep(time.Millisecond * time.Duration(rand.Intn(1000)))
			out <- i
			i ++
		}
	}()
	return out
}

func main() {

	var ch1, ch2 = genChan(), genChan()
	for {
		select {
			case data := <- ch1:
				fmt.Println("CH1 的数据:", data)
			case data := <-ch2:
				fmt.Println("CH2 的数据:", data)
		}
	}
}

输出结果(部分)

代码语言:txt
AI代码解释
复制
CH1 的数据: 0
CH2 的数据: 0
CH1 的数据: 1
CH2 的数据: 1
CH1 的数据: 2
CH2 的数据: 2
CH1 的数据: 3
CH2 的数据: 3
CH1 的数据: 4
CH2 的数据: 4
CH1 的数据: 5
CH2 的数据: 5
CH2 的数据: 6
CH1 的数据: 6
CH1 的数据: 7
CH1 的数据: 8
CH2 的数据: 7
CH1 的数据: 9
CH2 的数据: 8
CH1 的数据: 10
CH2 的数据: 9
CH1 的数据: 11
CH1 的数据: 12
CH1 的数据: 13
CH2 的数据: 10
CH2 的数据: 11
CH1 的数据: 14
CH2 的数据: 12
CH2 的数据: 13
CH1 的数据: 15

Process finished with exit code 130 (interrupted by signal 2: SIGINT)

这就是select的一个应用场景,从输出结果可以看到,CH1、CH2的输出结果不一样,谁先出数据就先选择谁;两个同时出就随机的选择一个。

2、定时器的使用

比如上面的这段代码我想要在10秒之后程序就终止,我该如何处理呢?我们这里需要介绍一下Go语言的 time.After

代码语言:txt
AI代码解释
复制
// After waits for the duration to elapse and then sends the current time
// on the returned channel.
// It is equivalent to NewTimer(d).C.
// The underlying Timer is not recovered by the garbage collector
// until the timer fires. If efficiency is a concern, use NewTimer
// instead and call Timer.Stop if the timer is no longer needed.
func After(d Duration) <-chan Time {
	return NewTimer(d).C
}

从源码来看,他的返回值类型是一个 <-chan Time ,那就方便很多了

代码语言:txt
AI代码解释
复制
package main

import (
	"fmt"
	"time"
)

func genChan() chan int {

	out := make(chan int)
	go func() {

		i := 0
		for {
			time.Sleep(time.Second)
			out <- i
			i ++
		}
	}()
	return out
}

func main() {

	var ch1, ch2 = genChan(), genChan()
	tm := time.After(10 * time.Second) // 加上10秒的定时
	for {
		select {
			case data := <- ch1:
				fmt.Println("CH1 的数据:", data)
			case data := <-ch2:
				fmt.Println("CH2 的数据:", data)
			case <-tm:
				return // 收到指令程序直接return
		}
	}
}

运行到10秒,代码自动退出。

9.3、传统同步机制

Go语言除了CSP模型外,还是有传统同步机制的,比如互斥量 Mutex ,现在我们就用它举个例子:

用互斥量实现 atomic

代码语言:txt
AI代码解释
复制
package main

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

type atomicInt struct {
	value int
	lock sync.Mutex
}

func increment(a *atomicInt) {
	a.lock.Lock()
	defer a.lock.Unlock()
	a.value ++
}

func get(a *atomicInt) int {
	a.lock.Lock()
	defer a.lock.Unlock()
	return a.value
}

func main() {

	var a atomicInt
	increment(&a)
	go func() {
		increment(&a)
	}()
	time.Sleep(time.Second)
	fmt.Println(get(&a))
}

结果输出

代码语言:txt
AI代码解释
复制
2

Process finished with exit code 0

代码写完,可以用上面介绍的race来检查一下,是否有冲突,是否安全;当然这里还是不建议自己来造这些轮子的,直接使用系统的就可以了。系统提供了 atomic.AddInt32() 等等这些原子操作。

10. Http及其他标准库

10.0、Http标准库介绍

Go语言有很多的标准库,http库是最重要的之一,它对Http的封装也是非常完善的,之前我们有演示过服务端的一些基础使用,下面我们介绍一些客户端相关的使用

1、使用http客户端发送请求
代码语言:txt
AI代码解释
复制
package main

import (
	"net/http"
	"net/http/httputil"
	"fmt"
)

func main() {

	response, err := http.Get("https://www.shengguocun.com")
	if err != nil{
		panic(err)
	}
	defer response.Body.Close()

	ss, err := httputil.DumpResponse(response, true)
	if err != nil {
		panic(err)
	}
	fmt.Printf("%s \n", ss)
}

这里就把完整的头信息以及html的部分打印出来了。比如在我们现实的情境中,我们会根据UA做一些反作弊的策略,以及是否需要重定向到 M 端等等。这里的 http.Client 就能实现。

2、使用http.Client控制请求头

请求头信息直接通过 request.Header.Add 添加就可以了

代码语言:txt
AI代码解释
复制
package main

import (
	"net/http"
	"net/http/httputil"
	"fmt"
)

func main() {

	request, err := http.NewRequest(http.MethodGet,"https://www.shengguocun.com", nil)
	request.Header.Add("User-Agent", "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_12_4) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/66.0.3359.139 Safari/537.36")
	response, err := http.DefaultClient.Do(request)
	if err != nil{
		panic(err)
	}
	defer response.Body.Close()

	ss, err := httputil.DumpResponse(response, true)
	if err != nil {
		panic(err)
	}
	fmt.Printf("%s \n", ss)
}

上面我们都用的是 DefaultClient ,我们也可以自己创建 client, 首先我们先看一下 Client 内部都有些什么

查看源码我们发现有一个 CheckRedirect ,我们发现这是一个检查重定向的函数。那我们就用它做一下演示

代码语言:txt
AI代码解释
复制
package main

import (
	"net/http"
	"net/http/httputil"
	"fmt"
)

func main() {

	request, err := http.NewRequest(http.MethodGet,"https://jim-sheng.github.io", nil)
	request.Header.Add("User-Agent", "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_12_4) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/66.0.3359.139 Safari/537.36")
	client := http.Client{
		CheckRedirect: func(req *http.Request, via []*http.Request) error {
			fmt.Println("重定向地址:", req)
			return  nil
		},
	}
	response, err := client.Do(request)
	if err != nil{
		panic(err)
	}
	defer response.Body.Close()

	ss, err := httputil.DumpResponse(response, true)
	if err != nil {
		panic(err)
	}
	fmt.Printf("%s \n", ss)
}

输出结果(部分)

代码语言:txt
AI代码解释
复制
重定向地址: &{GET https://www.shengguocun.com/  0 0 map[User-Agent:[Mozilla/5.0 (Macintosh; Intel Mac OS X 10_12_4) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/66.0.3359.139 Safari/537.36] Referer:[https://jim-sheng.github.io]] <nil> <nil> 0 [] false  map[] map[] <nil> map[]   <nil> <nil> 0xc42012c090 <nil>}
HTTP/2.0 200 OK
Accept-Ranges: bytes
Access-Control-Allow-Origin: *

我们可以看到具体的重定向的地址 https://www.shengguocun.com/ ,其它的

代码语言:txt
AI代码解释
复制
// Transport specifies the mechanism by which individual
// HTTP requests are made.
// If nil, DefaultTransport is used.
Transport RoundTripper

主要用于代理

代码语言:txt
AI代码解释
复制
// Jar specifies the cookie jar.
//
// The Jar is used to insert relevant cookies into every
// outbound Request and is updated with the cookie values
// of every inbound Response. The Jar is consulted for every
// redirect that the Client follows.
//
// If Jar is nil, cookies are only sent if they are explicitly
// set on the Request.
Jar CookieJar

主要用于模拟登录用的

代码语言:txt
AI代码解释
复制
// Timeout specifies a time limit for requests made by this
// Client. The timeout includes connection time, any
// redirects, and reading the response body. The timer remains
// running after Get, Head, Post, or Do return and will
// interrupt reading of the Response.Body.
//
// A Timeout of zero means no timeout.
//
// The Client cancels requests to the underlying Transport
// using the Request.Cancel mechanism. Requests passed
// to Client.Do may still set Request.Cancel; both will
// cancel the request.
//
// For compatibility, the Client will also use the deprecated
// CancelRequest method on Transport if found. New
// RoundTripper implementations should use Request.Cancel
// instead of implementing CancelRequest.
Timeout time.Duration

主要设置超时的

3、http服务器性能分析

还是使用之前的代码

代码语言:txt
AI代码解释
复制
package main

import (
	"net/http"
	"os"
	"io/ioutil"

	_ "net/http/pprof"
)

type appHandler func(writer http.ResponseWriter, request *http.Request) error

func errWrapper(handler appHandler) func(http.ResponseWriter, *http.Request) {

	return func(writer http.ResponseWriter, request *http.Request) {


		err := handler(writer, request)
		if err != nil {
			switch {

				case os.IsNotExist(err):
					http.Error(writer, http.StatusText(http.StatusNotFound), http.StatusNotFound)
			}
		}
	}
}

func main() {

	http.HandleFunc("/list/",
		errWrapper(func(writer http.ResponseWriter, request *http.Request) error {
			path := request.URL.Path[len("/list/"):]
			file, err := os.Open(path)
			if err != nil {
				return err
			}
			defer  file.Close()

			all, err := ioutil.ReadAll(file)
			if err != nil {
				return err
			}

			writer.Write(all)
			return nil
		}))

	err := http.ListenAndServe(":2872", nil)
	if err != nil {
		panic(err)
	}
}

还是一样的代码,只不过import多了一个 import _ "net/http/pprof" , 为什么会多一个下划线呢?因为代码没有使用到,会报错,加一个下划线就可以了。重启代码,我们就可以访问 http://localhost:2872/debug/pprof/ 了

里面的 stacktrace 都可以查看到

我们可以查看 pprof 的源码,继续查看它的其他的使用方式

代码语言:txt
AI代码解释
复制
// Or to look at a 30-second CPU profile:
//
//	go tool pprof http://localhost:6060/debug/pprof/profile

比如这一段,我们可以查看30秒的CPU的使用情况。可以终端敲下该命令(替换成自己的监听的端口),获取出结果后敲下 web 命令就可以看下具体的代码哪些地方需要优化。其他的使用使用方式就不一一罗列了,有兴趣可以继续查阅。

10.1、其他库

其它的标准库就不过多罗列了, https://studygolang.com/pkgdoc 上面的中文版的文档已经非常详细了 。

11. 总结

Go语言给我们展现了不一样的世界观,没有类、继承、多态、重载,没有构造函数,没有断言,没有try/catch等等;上面是在学习Go语言的过程中,记录下来的笔记;也可能有部分地方存在偏颇,还望指点~~~

原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。

如有侵权,请联系 cloudcommunity@tencent.com 删除。

原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。

如有侵权,请联系 cloudcommunity@tencent.com 删除。

评论
登录后参与评论
1 条评论
热度
最新
刚刚看了上过来的,谢谢楼主的分享
刚刚看了上过来的,谢谢楼主的分享
回复回复点赞举报
推荐阅读
编辑精选文章
换一批
第2期 | 国内脑机接口领域专家教授汇总(修改)
第1期分享出来后,很多朋友在后台留言表示,这个汇总真是太有用了。说实话自己做的一些事能给他人带去帮助,是非常的开心。
脑机接口社区
2020/08/10
2K0
重磅!Nature子刊:利用GAN来​“深度伪造大脑数据”可以改善残疾人的脑机接口
近日,南加州大学(Universityof Southern California)维特比工程学院的研究人员正在使用生成对抗网络(GAN)来改善残疾人的脑机接口。生成对抗网络(Generative Adversarial Networks, GAN)是一种生成式模型,以创建深度伪造视频和逼真的人脸而闻名。
脑机接口社区
2022/08/18
4780
重磅!Nature子刊:利用GAN来​“深度伪造大脑数据”可以改善残疾人的脑机接口
重磅长文!先进院李骁健等人:在体神经界面技术的发展-从小到大规模记录
摘要:大脑功能的实现来自于分布在多个脑区的神经集群的协同活动。神经元活动的电位可以被电极捕获并传送给计算机处理,经过信号解析提取出脑内的信息。同时对多个脑区的神经元电位信号进行大量且密集的记录,很有助于解析特定行为的神经集群编码原理。在这篇综述中,我们重点介绍应用于从小规模到大规模记录的神经界面技术,并讨论面向更大规模神经集群的记录技术在微系统、电极器件和界面材料上的技术挑战与研发策略。
脑机接口社区
2022/09/22
1.8K0
重磅长文!先进院李骁健等人:在体神经界面技术的发展-从小到大规模记录
一种灵活,坚固且无凝胶的脑电图电极,可用于无创脑机接口
脑机接口(BCI)能够在大脑和电子设备之间实现直接和近乎即时的通信。目前最大的挑战之一是开发一种有效的无创BCI,它能使记录电极避免人类皮肤上的毛发,同时又不带来使用导电凝胶的不便和隐患。在这项研究中,清华大学研究人员开发了一种低成本、易于制造、柔韧、坚固且不含凝胶的脑电图(EEG)电极【银纳米线/聚乙烯醇缩丁醛(PVB)/三聚氰胺海绵(AgPMS)】,可以解决头发问题。由于银纳米线(AgNWs)表面金属化,海绵在重量不变的情况下导电率高达917 S/m。柔软的海绵框架和自锁式AgNW结合在一起,为新电极提供了非常好的机械稳定性(电导率在10%的压缩下循环10000次后保持不变)。基于稳态视觉诱发电位(SSVEP)在无毛皮肤上的BCI应用表明,新电极的BCI精度(86%)与导电凝胶支撑的传统电极(88%)大致相同。最重要的是,AgPMS在多毛皮肤上的性能并没有明显降低,这表明新电极可以替代传统电极用于无毛和多毛皮肤BCI及其他EEG应用。
脑机接口社区
2022/08/18
5690
一种灵活,坚固且无凝胶的脑电图电极,可用于无创脑机接口
基于SSVEP脑机接口控制的柔性机器人手套用于脑卒中患者的手功能康复
近日,香港大学深圳医院研究团队开展了一研究,探索了长期使用基于SSVEP脑机接口控制的柔性机器人手套对于手功能康复的影响,为新型的脑控机器人康复系统进行功能和神经康复训练的研究提供积极的启示。相关研究成果已发表在《IEEE TRANSACTIONS ON NEURAL SYSTEMS AND REHABILITATION ENGINEERING》。
脑机接口社区
2024/01/04
4290
基于SSVEP脑机接口控制的柔性机器人手套用于脑卒中患者的手功能康复
运动想象脑机接口编码:运动想象的执行与能力评估及提高方法 || 伏云发教授等
运动想象(MI)是驱动脑机接口(BCI)的一种重要范式,但 MI 心理活动不易控制或习得,MI-BCI 的性能严重依赖被试 MI 的表现。因此 MI 心理活动的正确执行以及能力的评估和提高对 MI-BCI 系统性能的提升及应用具有重要的甚至关键性的作用。然而,在 MI-BCI 的研发中,已有研究主要聚焦于解码 MI 的算法,对 MI 心理活动的这三个方面没有足够的重视。本文针对 MI-BCI 的这些问题进行详细论述,指出被试易把动觉运动想象执行为视觉运动想象;未来需要研发客观的、定量可视化的 MI 能力评估方法,并且需要研发有效的、训练时间短的 MI 能力提高方法,也需要在一定程度上解决个体之间和内部 MI 的差异性、共性和 MI-BCI 盲问题。
脑机接口社区
2022/09/22
2.5K0
运动想象脑机接口编码:运动想象的执行与能力评估及提高方法 || 伏云发教授等
国外研究团队对EEG+fNIRS的脑机接口在执行运动想象任务时的性能表现的相关研究
脑机接口(Brain-Computer Interfaces,BCI)已被证明是一种很前景广阔的神经康复工具。然而,基于传统方法的BCI准确率和可靠性不高,不同的大脑活动模式并不是对所有的BCI用户都是最优的,而且信息传递率低。多项研究表明,结合不同的脑信号采集方法可以提高脑机接口的性能。
脑机接口社区
2022/09/22
1.3K0
国外研究团队对EEG+fNIRS的脑机接口在执行运动想象任务时的性能表现的相关研究
个性化脑机接口及应用
脑机接口(BCI)是一种变革传统人机交互的新型技术,用户的大脑是直接的控制信号源。在BCI转化为实际应用时,由于用户个体之间的感觉、知觉、表象与认知思维活动、脑结构与功能具有一定的差异,通用BCI难以满足不同个体的需求。为此,需要为特定用户定制个性化的BCI。迄今为止,少有文献对个性化BCI涉及的关键科学与技术问题进行阐述,本文聚焦个性化BCI,给出个性化BCI的定义,详述其设计研发以及评价方法和应用,并讨论个性化BCI面临的挑战及未来方向。期望本文对个性化BCI创新研究及实用化提供有益的思路。
脑机接口社区
2023/02/14
6520
个性化脑机接口及应用
​脑机接口(BCI)与人工智能:仅用思想来控制周围事物是什么感觉?
如今高新技术实验室里,每天都在上演人机交互的过程,最常见的,残疾人通过训练自己的思想来控制机器人的四肢。而人类期望有一天能够用我们的思想操纵宇宙飞船,将我们的大脑下载到电脑上,并最终创造出半机器人。特斯拉和SpaceX的首席执行官收购了Neuralink公司,旨在建立大脑和计算机之间的直接联系。随着过去几十年科技的迅猛发展,人类和机器之间的界限已经开始缩小。在机器的帮助下,科幻小说中壮观的精神控制世界慢慢向现实靠近。目前这些新技术的前沿是脑机接口(BCI)和人工智能(AI),虽然BCIs和AI以往是相互独立开发和应用的。但是,现在越来越多的科学家们希望将两者结合起来,使脑电信号操纵外部设备过程更高效。
脑机接口社区
2022/08/26
1K0
​脑机接口(BCI)与人工智能:仅用思想来控制周围事物是什么感觉?
华中科技大学伍冬睿教授:非侵入式脑机接口中的迁移学习综述(2016-2020)
今天Rose小哥介绍关于华中科技大学伍冬睿教授关于非侵入式脑机接口中的迁移学习综述(2016-2020)。本文章经伍教授授权在脑机接口社区转载。
脑机接口社区
2020/07/01
2K0
​基于BCI的现代神经反馈有助于认知增强
神经反馈是一种人类增强技术,旨在提供心理变量(例如记忆力,注意力,处理速度或执行功能)的认知改善。
脑机接口社区
2023/02/13
4180
​基于脑机接口的闭环运动想象脑电图仿真
脑机接口(BCI),尤其是能够解码运动意图的脑机接口,由于其作为神经修复系统的潜力,能够改善患有各种运动功能损害病症(如脊髓损伤、肌萎缩侧索硬化症和中风)的患者的生活质量,已经成为积极研究的热门主题。一种成熟的方法是基于感觉运动节律(SMR)的运动想象BCI,它允许用户通过检测和解码与真实和想象的运动相关的SMR模式来控制物理或虚拟世界中仿真的运动。通常在BCI系统中,解码算法的测试、任务及其参数对于优化性能至关重要,然而,当研究广泛的参数集,进行人体实验既昂贵又耗时,而尝试利用以前收集到的数据线下分析却又缺乏系统和用户之间自适应反馈循环,极大限制了其适用性。因此,已有许多研究已经试图通过实时神经活动模拟器解决这一问题。
脑机接口社区
2023/02/14
7040
​基于脑机接口的闭环运动想象脑电图仿真
脑机接口--用于将音乐与思想进行合成
神经接口代表了科学研究领域中一个越来越热门的话题:当前我们对脑机接口的认知是医学应用和人体增强的新领域,这在残疾人辅助技术领域中非常重要。而实际上,脑机接口(BCI)是一种工具,它无需用户任何随意的肌肉控制即可与周围环境进行交互和通信。正是由于这个原因,BCI通常用作患有严重残疾的人的辅助设备,这些人由于脑损伤,脊髓损伤或神经运动退化而无法通过通常可用的通道进行交流。
脑机接口社区
2020/06/30
7840
脑机接口--用于将音乐与思想进行合成
Nature子刊:对EcoG脑机接口进行无监督适应
脑机接口目前有一些明显的缺陷,这使得无法在日常场景中得到广泛运用,例如在进行监督学习时,被试常被要求进行特定的动作。但这会出现几个问题:
脑机接口社区
2023/09/08
2140
Nature子刊:对EcoG脑机接口进行无监督适应
EEG-MI 基于EEG信号的运动想象分类实验
“脑机接口”(Brain Cpmputer Interface, BCI)研究的核心是在大脑和外部设备之间建立直接的联通通路,因此脑机接口也被誉为“人工智能的顶级科学”。脑机接口(BCI)通过计算机信息处理技术理解人的意图,并将此意图转化为对外界的控制命令,实现大脑对外部世界的直接控制。目前的脑机接口应用主要有以下几个方面[1]:
脑机接口社区
2020/07/29
2.3K0
EEG-MI 基于EEG信号的运动想象分类实验
北理工团队创建自然场景下声音目标探测的听觉脑机接口
近日,北京理工大学机械与车辆学院毕路拯教授团队创建了自然场景下声音目标探测的听觉脑机接口。研究成果以“Sound Target Detection under Noisy Environment Using Brain-Computer Interface”为题,被国际顶级期刊《IEEE Transactions on Neural Systems and Rehabilitation Engineering》录取。论文第一作者为其团队硕士研究生王瑞东。
脑机接口社区
2023/02/14
4680
北理工团队创建自然场景下声音目标探测的听觉脑机接口
基于增强现实和脑机接口的机械臂控制系统
对于有运动障碍或身体残疾的人来说,完成日常任务和家务是非常具有挑战性的。机器人技术的最新进展,例如大脑控制的机器人肢体,有可能显著改善他们的生活质量。
脑机接口社区
2022/08/18
5060
基于增强现实和脑机接口的机械臂控制系统
基于无线EEG的脑机接口和新型干式传感器进行游戏控制
脑机接口(BCI)是一种通信系统,通过将大脑信号转换成机器指令,帮助用户与外部环境进行交互。脑电信号的可用性和可靠性使其成为脑机接口最常用的方法。许多基于脑电图的脑机接口设备都是利用传统的湿式或微机电系统(MEMS)型脑电图传感器开发的。然而,这些传统的传感器接触皮肤时会令人产生不舒服的感受。因此,以舒适、方便的方式获取脑电信号是一种新型BCI器件的重要组成部分。在本研究中,作者开发了一种基于可穿戴、无线和便携式脑电图仪的BCI设备,该设备具有基于干式脑电图传感器,并通过游戏控制应用程序进行了演示。干式脑电图传感器无导电胶;然而,他们能够提供良好的导电性,能够通过适应不规则的皮肤表面和保持适当的皮肤传感器阻抗在前额部位有效地获取脑电图信号。作者还演示了使用提出的便携式设备进行游戏控制的实时认知阶段检测应用。研究结果表明,利用这种基于脑电图的便携式脑机接口装置,可以方便、有效地控制外界,为康复工程的研究提供了一条途径。
脑机接口社区
2022/08/18
6190
基于无线EEG的脑机接口和新型干式传感器进行游戏控制
CNN+LSTM--一种运动想象分类新模型
说到运动想象(motor imagenation, MI), 我们都很熟悉,它是指个体在心理上模拟给定动作时的动态状态。如何通过运动想象的脑电信号来分类个体的心理意图,一直是研究人员关注的重点,MI信号可以用于控制外部设备,如大脑控制的机器人、大脑控制的外骨骼、自动驾驶汽车等, 因此提高MI信号的分类准确性是极其有意义的。
脑机接口社区
2023/02/14
2.8K1
CNN+LSTM--一种运动想象分类新模型
社论:运用基于脑-机接口(BCI)的认知和运动控制,改善老年人的健康和福祉
Keywords:brain computer interface, brain aging, BCI (brain control interface), EEG, stroke, Alzheimer, post-stroke
脑机接口社区
2022/09/22
5290
社论:运用基于脑-机接口(BCI)的认知和运动控制,改善老年人的健康和福祉
推荐阅读
第2期 | 国内脑机接口领域专家教授汇总(修改)
2K0
重磅!Nature子刊:利用GAN来​“深度伪造大脑数据”可以改善残疾人的脑机接口
4780
重磅长文!先进院李骁健等人:在体神经界面技术的发展-从小到大规模记录
1.8K0
一种灵活,坚固且无凝胶的脑电图电极,可用于无创脑机接口
5690
基于SSVEP脑机接口控制的柔性机器人手套用于脑卒中患者的手功能康复
4290
运动想象脑机接口编码:运动想象的执行与能力评估及提高方法 || 伏云发教授等
2.5K0
国外研究团队对EEG+fNIRS的脑机接口在执行运动想象任务时的性能表现的相关研究
1.3K0
个性化脑机接口及应用
6520
​脑机接口(BCI)与人工智能:仅用思想来控制周围事物是什么感觉?
1K0
华中科技大学伍冬睿教授:非侵入式脑机接口中的迁移学习综述(2016-2020)
2K0
​基于BCI的现代神经反馈有助于认知增强
4180
​基于脑机接口的闭环运动想象脑电图仿真
7040
脑机接口--用于将音乐与思想进行合成
7840
Nature子刊:对EcoG脑机接口进行无监督适应
2140
EEG-MI 基于EEG信号的运动想象分类实验
2.3K0
北理工团队创建自然场景下声音目标探测的听觉脑机接口
4680
基于增强现实和脑机接口的机械臂控制系统
5060
基于无线EEG的脑机接口和新型干式传感器进行游戏控制
6190
CNN+LSTM--一种运动想象分类新模型
2.8K1
社论:运用基于脑-机接口(BCI)的认知和运动控制,改善老年人的健康和福祉
5290
相关推荐
第2期 | 国内脑机接口领域专家教授汇总(修改)
更多 >
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档