Golang学习笔记

常用占位符

https://www.jianshu.com/p/66aaf908045e

问题记录

1. 接口

1.1 sort.Reverse如何实现逆排序?

golang: sort.Sort(sort.Reverse(n))是怎么进行逆排序? - soolaugust的回答 - 知乎 https://www.zhihu.com/question/360515958/answer/932843144

2. Goroutine、Channel

2.1 all goroutines are asleep - deadlock!

func main() {
	chnl := make(chan string, 10)
	chnl <- "a"
	chnl <- "b"
	chnl <- "c"
	chnl <- "d"
	chnl <- "e"
	flag := make(chan int, 1)
	go func() {
		for s := range chnl {
			fmt.Println("1", s)
		}
		flag <- 1
		fmt.Println("flag1done")
	}()
	<-flag
	go func() {
		for s := range chnl {
			fmt.Println("2", s)
		}
		flag <- 1
		fmt.Println("flag2done")
	}()
	<-flag
	fmt.Println("all done")
}

输出如下:

1 a
1 b
1 c
1 d
1 e
fatal error: all goroutines are asleep - deadlock!

注意到flag1done甚至没有输出,说明第一个协程中的for循环没有结束,导致flag<-1没有执行。而flag中一直没有写入,主协程中的读取就会阻塞,导致死锁。

原因:range遍历Channel时依次从Channel接收数据,当Channel被关闭且没有数据可接收时跳出遍历。所以,只有先主动关闭chnl来跳出range循环,才可以让第一个协程往flag写数据。

只需要在一开始往chnl写完数据后close(chnl)就可以解决了。现在输出如下:

1 a
1 b
1 c
1 d
1 e
flag1done
flag2done
all done

在上面的代码我让第一个协程完成任务后再发起第二个协程,现在做如下改动,让两个协程并发遍历chnl

flag := make(chan int, 1)
go func() {
    for s := range chnl {
        fmt.Println("1", s)
    }
    // flag <- 1
    fmt.Println("flag1done")
}()
// <-flag
go func() {
    for s := range chnl {
        fmt.Println("2", s)
    }
    flag <- 1
    fmt.Println("flag2done")
}()
<-flag
fmt.Println("all done")

此时输出如下:

1 b
1 c
1 d
1 e
flag1done
2 a
flag2done
all done

也说明协程之间通过Channel读取数据可以不用考虑线程安全性,因为一个数据被读取出来后,管道里也会对应地少一个数据。(对吗?)

2.2 并发的退出

读go圣经8.9章“并发的退出”的时候,有些一开始搞混的地方,这里做记录。

Channel广播机制用来通知goroutine关闭:

回忆一下我们关闭了一个channel并且被消费掉了所有已发送的值,操作channel之后的代码可以立即被执行,并且会产生零值。我们可以将这个机制扩展一下,来作为我们的广播机制:不要向channel发送值,而是用关闭一个channel来进行广播。

var done = make(chan struct{})

func cancelled() bool {
    select {
    case <-done:
        return true
    default:
        return false
    }
}

这里的done我们并不往里面写值,只是在需要通知goroutine关闭的时候close(done),这样cancelled函数就会返回true了。在goroutine里可以通过cancelled函数的返回值判断是否结束工作。

3. 共享变量

数据竞争的定义:

数据竞争会在两个以上的goroutine并发访问相同的变量且至少其中一个为写操作时发生。

4. Goroutines和线程

从动态栈、Goroutine调度、GOMAXPROCS、有无ID号四方面,详见《Go语言圣经》9.8章:https://docs.hacknode.org/gopl-zh/ch9/ch9-08.html

posted @ 2021-11-23 11:04  damao_33  阅读(40)  评论(0编辑  收藏  举报