字符串
字符串类型属于预定义类型,所以不能直接修改字符串。字符串类型代表了一个字符串集合,在底层一个字符串值就是一个字节序列。字符串的长度就是字节序列中字节的个数(中文三个字节,英文一个字节)。一个字符串长度在编译期间就能确定。使用range 就是unicode 输出(即中文三个字节,英文一个字节)。
函数
函数也是一个变量,比如 将一个匿名函数赋值给一个变量,那么这个变量每次调用都会更新内部的值。
eg
func intSeq() func() int {
i := 0
return func() int {
i++
return i
}
}
func main() {
f := intSeq()
fmt.Println(f())
fmt.Println(f())
fmt.Println(f())
f2 := intSeq()
fmt.Println(f2())
}
output:
1
2
3
1
通道
通道用完一定要关闭。
no-blocking channel
常规的通过通道接受和发送数据是阻塞的,我们可以使用default来实现非阻塞的发送和接受数据,当然 使用非缓冲通道个人认为是一个更好的选择。
func main() {
messages := make(chan string)
signals := make(chan bool)
select {
case msg := <-messages:
fmt.Println("received message", msg)
default:
fmt.Println("no message received")
}
msg := "hi"
select {
case messages <- msg:
fmt.Println("sent message", msg)
default:
fmt.Println("no message send")
}
select {
case msg := <-messages:
fmt.Println("received messafe", msg)
case sig := <-signals:
fmt.Println("received signal", sig)
default:
fmt.Println("no activity")
}
time.Sleep(time.Second)
}
打印结果是
no message received
no message send
no activity
msg := "hi"
select {
case messages <- msg:
fmt.Println("sent message", msg)
default:
fmt.Println("no message send")
}
操作之所以打印no message send" 是因为没有其他goroutine接收数据,所以无法写入数据,如果没有select会造成死锁,如果进入第一个分支,可以使用缓冲通道或者(开启一个读取通道goroutine 而且必须使用runtime.GOsched 让开启的协程有机会运行)。
eg
go func() {
// <-messages
fmt.Println("Received from other go routine", <-messages)
}()
runtime.Gosched() // 必须存在,让goroutine有机会运行
msg := "hi"
select {
case messages <- msg:
fmt.Println("sent message", msg)
default:
fmt.Println("no message send")
}
通道关闭
只有缓冲通道才需要关闭,关闭的通道不能再读取数据,这通常用在协调接受者.可以关闭非空的通道
速率限制
对每个请求采用 time.Tick 进行速率限制
使用golang http 协议直接开启 协程处理请求,还怎么在代码段限制速率?
微服务设计:速率限制,熔断,降级
同步
通过通信实现共享内存,一块数据只能被唯一的一个goroutine所有。(*查看一下怎么理解通过通信共享内存)
eg:并发的map
go func() {
var state = make(map[int]int)
for {
select {
case read := <-reads:
read.resp <- state[read.key]
case write := <-writes:// 更新map都是在这里进行同步,不会造成并发map不安全问题
state[write.key] = write.val
write.resp <- true
}
}
}()
share memory by communicating,not commnnicating by sharing
通信来共享内存:这种情况一般而言是通过channel传送数据指针,并且约定传送之后不再修改这个数据。
共享内存通信:类似声明一个全局变量,在每个协程访问的时候加锁
不过这不是一定的,在合适的地方使用合适的方式.
不同线程不共享内存不用锁,线程之间通信和同步都是用channel
eg:
type readOp struct {
key int
resp chan int
}
go func() {
var state = make(map[int]int)
for {
select {
case read := <-reads:
read.resp <- state[read.key]
case write := <-writes:
state[write.key] = write.val
write.resp <- true
}
}
}()
for r := 0; r < 100; r++ {
go func() {
for {
read := &readOp{
key: rand.Intn(5),
resp: make(chan int)}
reads <- read
<-read.resp
atomic.AddUint64(&readOps, 1)
time.Sleep(time.Millisecond)
}
}()
}
通过channel 来控制map的访问而不是通过mutex
通过channel 通信传输的是指针,所以修改的时候 也是修改的元数据。
返回数据resp 为什么用chan 类型因为 只有chan 类型才能监控到数据已经返回,使用其他数据类型无法做到数据返回监控。
通过信息通信共享内存,而不是共享内存通信,如果用共享内存,可以使用map[] mutex.lock
.一定要注意channel 传递的是指针。。