goroutine简单示例
package main
import (
"fmt"
)
func main() {
ch := make(chan string)
for i := 0; i < 50000; i++ {
go PrintHelloWorld(i, ch)
}
for {
msg := <-ch
fmt.Println(msg)
}
}
func PrintHelloWorld(i int, ch chan string) {
for {
ch <- fmt.Sprintf("hello world from %d\n", i)
}
}
goroutine将结果通过chan 传给 主goroutine
当chan可能关闭的时候 接收方需要加入显示判断 , chan关闭时,返回第二个参数false
for {
if num, ok := <-p; ok {
fmt.Println(num)
} else {
break
}
}
接收chan 通过range简写, 当chan close之后range结束。 chan没有close则一直循环。
for v := range p {
fmt.Println(v)
}
声明chan参数 或返回值时 ,可以指明 chan的方向。
func ArraySource(a ...int) <-chan int {
out := make(chan int)
go func() {
for _, v := range a {
out <- v
}
close(out)
}()
return out
}
<- chan 代表只能从chan中取, 对于使用者而言。
io接口 Reader Writer
文件实现了io接口
file, err = os.Open(filename)
buffer := make([]byte, 8)
n, err := file.Read(buffer)
从文件中读取8个字节
file, err := os.Create(filename)
buffer := make([]byte, 8)
binary.BigEndian.PutUint64(buffer, uint64(v))
file.Write(buffer)
创建文件,写入一个int, 64位上为8字节
缓冲io
使用bufio包装,可以得到带缓存的writer reader
带缓存的writer最终要进行flush()
writer := bufio.NewWriter(file)
...
writer.Flush()
并发模型
将任务分解为多个子任务并发执行
func Run() {
lines := readlines()
var wg sync.WaitGroup
wg.Add(len(lines))
ch := make(chan string)
for _, line := range lines {
go func(l string) {
newLine := strings.ToUpper(l)
ch <- newLine
wg.Done()
}(line)
}
go func() {
wg.Wait()
close(ch)
}()
Display(ch)
}
func Display(ch chan string) {
fmt.Println("Display result *** ")
for c := range ch {
fmt.Printf(c)
}
fmt.Println("*****")
}
向chan中传入内容 回阻塞当前goroutine,直到其他goroutine到chan中获取
ch := make(chan string)
ch <- "result123" // blocking
从已经close的chan 中获取,会返回 空值。 可以通过返回的第二个参数判断 chan是否close
问题记录:
select 两个chan 的结果
使用select以先到先服务的方式,接收通道发送的消息
ch1 := doWork1()
ch2 := doWork2()
var ok1, ok2 bool
var r1, r2 string
for {
select {
case r1, ok1 = <-ch1:
fmt.Println(r1, ok1)
case r2, ok2 = <-ch2:
fmt.Println(r2, ok2)
}
if ok1 && ok2 {
break
}
}
select 中 ok1 := 导致每次select作用域中新建了ok1变量,select作用域外的ok1变量一直为false, 导致for select无法退出,最终死锁。
ch1 := doWork1()
ch2 := doWork2()
var ok1, ok2 bool
for {
select {
case r1, ok1 := <-ch1:
fmt.Println(r1, ok1)
case r2, ok2 := <-ch2:
fmt.Println(r2, ok2)
}
if ok1 && ok2 {
break
}
}
⚠️ select也会匹配已经关闭的chan。
ok1, ok2 默认是true, 当两个chan都关闭时为false, 退出for select循环。
var msg string
ok1, ok2 := true, true
for ok1 || ok2 {
select {
case msg, ok1 = <-c1:
if ok1 {
fmt.Printf("%s receive from c1\n", msg)
}
fmt.Println("case 1 ", ok1)
case msg, ok2 = <-c2:
if ok2 {
fmt.Printf("%s receive from c2\n", msg)
}
fmt.Println("case 2.. ", ok2)
}
fmt.Println(ok1, ok2)
}
int 转 string
使用 strconv.Itoa(u.Id)
string(10) 的结果不是字符串10, 而是10的ascii码字符
json.Marshal失败
json解析的字段 首字母必须为大写,否则解析错误
局部变量覆盖返回值变量
func scanFile(path string) (err error) {
if content, err := ioutil.ReadFile(path); err == nil {
name := filepath.Base(path)
log.Println("scan file : ", name)
b := blog.Blog{User_Id: 1, Title: strings.Split(name, ".")[0], Content: string(content)}
b.Create()
} else {
log.Println(err)
}
log.Println(err)
return
}
scanFile函数 if := 后有两个err变量 ,返回值的err变量一直为nil
将 := 修改为 = , 保证不会在子作用域下 新建一个err变量。
func scanFile(path string) (err error) {
var content []byte
if content, err = ioutil.ReadFile(path); err == nil {
name := filepath.Base(path)
log.Println("scan file : ", name)
b := blog.Blog{User_Id: 1, Title: strings.Split(name, ".")[0], Content: string(content)}
b.Create()
}
return
}
局部变量覆盖入参变量
入参被map的返回值覆盖,导致一个空的对象被加到map中。后续调用时会产生异常。
var sessions map[string]func() Session
func init() {
sessions = make(map[string]func() Session)
}
func Register(key string, creator func() Session) error {
if creator, ok := sessions[key]; ok {
return errors.New("dumplicate key " + key)
} else {
sessions[key] = creator
return nil
}
}
if creator, ok := sessions[key]; ok {
return errors.New("dumplicate key " + key)
} else {
}
中的第一个返回值 只能在 ok的分支中使用,不能在else中使用,会是一个无效值
只判断map中是否存在,应该忽略第一个返回值。
if _, ok := sessions[key]; ok {
return errors.New("dumplicate key " + key)
} else {
}
函数中入参包含 切片时,并且函数中进行append操作
入参 需要传入切片的 指针,才能保证函数返回后,切片改变。
go test 缓存
默认情况 当代码没有变化时,再次运行go test会使用缓存结果,不会实际执行。
关闭缓存可以使用 -count=1
网友评论