1. 延时与并发
本节,讨论延时和并发的关系。以下都以cpu资源足够的情况下讨论
假如一个接口,延时为x ms ,则一个并发,每秒可处理 k=100/x
如果需要处理m qps,则需要至少 m/k 个goroutine
事与愿违,当并发提高后,可能后面响应可能就增大,导致每秒处理量小于k
这个时候,整个系统就触摸到天花板了。
这个时候,增加并发,并不能解决问题,而是需要优化响应时间了。
2. 哲学家问题 的一种csp 解法
基本思路: 首先A为左边,B为右边 1. 哲学家会等待并锁定A边的叉 2. 如果B边没人用,则锁定B边 然后拿起两根叉,吃面 3. 如果B边被锁定,则解锁A边,A和B 交换,重复执行第一步骤
golang 代码实现可参考:
package main
import (
"fmt"
"time"
)
type gopher struct {
id int
count int
}
func (g *gopher) eat() {
fmt.Printf("%d eat\n", g.id)
g.count++
time.Sleep(time.Second / 10)
}
func (g *gopher) think() {}
type folk struct {
id int
available chan bool
}
var folks [5]folk
func (f *folk) lock() {
f.available <- true
}
func (f *folk) unlock() {
<-f.available
}
func (f *folk) trylock() bool {
select {
case f.available <- true:
return true
default:
return false
}
}
func (g *gopher) left() int {
return g.id
}
func (g *gopher) right() int {
return (g.id + 1) % len(folks)
}
func (g *gopher) get_folks() {
need := []*folk{
&folks[g.left()],
&folks[g.right()],
}
fmt.Printf("%d need folk %d %d\n", g.id, g.left(), g.right())
first := 0
for {
second := (first + 1) % len(need)
fmt.Printf("%d get folk %d\n", g.id, need[first].id)
//等待并取得一个叉
need[first].lock()
// 如果第二个叉没人用,则取,否则将之前取的叉放回去
if need[second].trylock() {
fmt.Printf("%d get folk %d\n", g.id, need[second].id)
return
}
fmt.Printf("%d giveup folk %d\n", g.id, need[first].id)
need[first].unlock()
//盯着别人手里的叉
first = second
}
}
func (g *gopher) put_folks() {
fmt.Printf("%d put folk %d %d\n", g.id, g.left(), g.right())
folks[g.left()].unlock()
folks[g.right()].unlock()
}
func (g *gopher) dine() {
fmt.Printf("%d start\n", g.id)
for {
g.think()
g.get_folks()
g.eat()
g.put_folks()
}
}
func main() {
gs := []*gopher{}
for i := 0; i < 5; i++ {
folks[i].available = make(chan bool, 1)
folks[i].id = i
g := &gopher{id: i}
gs = append(gs, g)
go g.dine()
}
time.Sleep(10 * time.Second)
fmt.Printf("done\n")
for i := 0; i < 5; i++ {
fmt.Printf("%d eat count %d\n", gs[i].id, gs[i].count)
}
}