go 注意事项

阅读更多
package main

import (
	"fmt"
	"io/ioutil"
	"log"
	"os"
	"runtime"
)

var msg string
var done = make(chan bool)

func main() {
	runtime.GOMAXPROCS(1)  //应用程序何以在运行期间设置运行时系统中得P最大数量

	//当参数的可变参数是空接口类型时,传人空接口的切片时需要注意参数展开的问题
	var a = []interface{}{1, 2, 3}
	fmt.Println(a)
	fmt.Println(a...)

	//在函数调用参数中,数组是值传递,无法通过修改数组类型的参数返回结果
	x := [3]int{1, 2, 3}
	func(arr [3]int) {
		arr[0] = 7
		fmt.Println(arr)
	}(x)
	fmt.Println(x)

	//map是一种hash表实现,每次遍历的顺序都可能不一
	m := map[string]string{
		"1": "1",
		"2": "2",
		"3": "3",
	}
	for k, v := range m {
		println(k, v)
	}

	//不同Goroutine之间不满足顺序一致性内存模型 要实现打印使用信号阻塞
	go setup()
	<-done
	fmt.Println(msg)

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


	//切片会导致整个底层数组被锁定,底层数组无法释放内存。如果底层数组较大会对内存产生很大的压力。
	headerMap := make(map[string][]byte)
	for i := 0; i < 5; i++ {
		name := "D:\\test.txt"
		data, err := ioutil.ReadFile(name)
		if err != nil {
			log.Fatal(err)
		}
		//如下  data内存 将无法释放
		//headerMap[name] = data[:1]
		//解决的方法是将结果克隆一份,这样可以释放底层的数组
		headerMap[name] = append([]byte{}, data[:1]...)  //append用来将元素添加到切片末尾并返回结果
		log.Println("headerMap:%+V",headerMap) //参数+V 打印接口
	}

	//recover捕获的是祖父级调用时的异常,直接调用时无效:必须在defer函数中直接调用才有效:
	defer func() {
		if r := recover(); r != nil {
			fmt.Printf("捕获到的错误:%s\n", r)
		}
	}()
	panic("1")

	//defer在函数退出时才能执行,在for执行defer会导致资源延迟释放   注释上面panic代码 执行下面代码
	//错误的代码
	//for i := 0; i < 5; i++ {
	//	f, err := os.Open("/path/to/file")
	//	if err != nil {
	//		log.Fatal(err)
	//	}
	//	defer f.Close()
	//}
	//解决的方法可以在for中构造一个局部函数,在局部函数内部执行defer:
	for i := 0; i<5; i++ {
		func() {
			f, err := os.Open("./text.txt")
			if err != nil {
				log.Fatal(err)
			}
			defer f.Close()
		}()
	}

	//runtime.Goexit函数被调用后,会立即使调用他的Groution的运行被终止,但其他Goroutine并不会受到影响
	//runtime.Gosched函数的作用是暂停调用他的Goroutine的运行,调用他的Goroutine会被重新置于Gorunnable状态,并被放入调度器可运行G队列中
	//此处 若注释 runtime.Gosched()cpu 将会为for独占Goroutine 将不会运行    Goroutine是协作式抢占调度,Goroutine本身不会主动放弃CPU
	for {
		runtime.Gosched()
	}
	//此处 后面的代码不会执行

	//是通过阻塞的方式避免CPU占用
	//select{}
}

func setup() {
	msg = "hello, world"
	done <- true
}



Go语言是带内存自动回收的特性,因此内存一般不会泄漏。但是Goroutine确存在
泄漏的情况,同时泄漏的Goroutine引用的内存同样无法被回收
上面的程序中后台Goroutine向管道输入自然数序列,main函数中输出序列。但是
当break跳出for循环的时候,后台Goroutine就处于无法被回收的状态了

func main(){
	ch := func() <-chan int {
		ch := make(chan int)
		go func() {
			for i := 0; ; i++ {
				ch <- i
			}
		} ()
		return ch
	}()

	for v := range ch {
		fmt.Println(v)
		if v == 5 {
			break
		}
	}
}


可以通过context包来避免这个问题(Goroutine内存泄漏的情况)
当main函数在break跳出循环时,通过调用 cancel() 来通知后台Goroutine退出,
这样就避免了Goroutine的泄漏

func main() {
	ctx, cancel := context.WithCancel(context.Background())
	ch := func(ctx context.Context) <-chan int {
		 ch := make(chan int)
		 go func() {
			 for i := 0; ; i++ {
				 select {
				 case <- ctx.Done():
					 return
				 case ch <- i:
				 }
			 }
		 } ()
		 return ch
	}(ctx)
 
	for v := range ch {
		 fmt.Println(v)
		 if v == 5 {
			 cancel()
			 break
		 }
	}
 }


Context 内容

type Context interface {
    Deadline() (deadline time.Time, ok bool)
    Done() <-chan struct{}
    Err() error
    Value(key interface{}) interface{}
}


Deadline 会返回一个超时时间,Goroutine获得了超时时间后,例如可以对某些io操作设定超时时间。

Done 方法返回一个信道(channel),当 Context 被撤销或过期时,该信道是关闭的,即它是一个表示Context是否已关闭的信号。

当 Done 信道关闭后, Err 方法表明 Contex t被撤的原因。

Value 可以让Goroutine共享一些数据,当然获得数据是协程安全的。但使用这些数据的时候要注意同步,比如返回了一个map,而这个map的读写则要加锁

func WithCancel(parent Context) (ctx Context, cancel CancelFunc)
func WithDeadline(parent Context, deadline time.Time) (Context, CancelFunc)
func WithTimeout(parent Context, timeout time.Duration) (Context, CancelFunc)
func WithValue(parent Context, key interface{}, val interface{}) Context



你可能感兴趣的:(go,游戏,情感,资讯)