赞
踩
type Context interface { // 当Context被取消或者到了deadline,返回一个被关闭的channel // 它是一个只读的channel,也就是说在整个生命周期都不会有写入操作,只有当这个channel被关闭时。 // 才会读取到这个channel对应类型的零值,否则是无法取到任何值的。 // 正是因为这个机制,当子协程从这个channel中读取到零值后可以做一些收尾工作,让子协程尽快推出 Done() <-chan struct{} // 在channel Done 关闭后,返回Context取消原因,这里只有两种原因:1.取消 2.超时 Err() error // 返回Context是否会被取消以及自动取消时间(即deadline) // 通过这个时间我们可以判断是否有必要进行接下来的操作,如果剩余时间太短则可以选择不继续执行一些任务, // 可以节省系统资源 Deadline() (deadline time.Time, ok bool) // 获取key对应的value Value(key interface{}) interface{} }
func slowOperation(ctx context.Context) (string, error) { // 获取Request id fmt.Println("get request_id from ctx", ctx.Value("request_id")) // 使用一个 select 语句来监听 Context 的状态变化 select { case <-time.After(3 * time.Second): // 模拟操作需要3s才能完成 return "Done", nil case <-ctx.Done(): // 如果Context 被取消或者超时,返回相应的错误信息 return "", ctx.Err() } } // 模拟一个HTTP服务器的处理函数,使用Context来控制超时 func handler(w http.ResponseWriter, r *http.Request) { requestID := uuid.New().String() fmt.Println("gen request_id ", requestID) // 传递request_id ctr := context.WithValue(r.Context(), "request_id", requestID) // 从请求中获取 Context 并设置一个 1 秒钟的超时时间 ctx, cancel := context.WithTimeout(ctr, 1*time.Second) // 在函数返回时调用取消函数 defer cancel() // 调用耗时操作,并传递Context result, err := slowOperation(ctx) if err != nil { // 如果操作失败,返回错误信息和状态码 http.Error(w, err.Error(), http.StatusGatewayTimeout) return } // 如果操作成功,返回结果 fmt.Fprintln(w, result) } func main() { // 创建一个HTTP服务器,并注册处理函数 http.HandleFunc("/", handler) fmt.Println("Server is running on http://localhost:9090") // 启动服务器,并监听9090端口 err := http.ListenAndServe(":9090", nil) if err != nil { fmt.Println(err) } }
type MyStruct struct {
// 不要在这里存储Context
}
func (m *MyStruct) myMethod(ctx context.Context, arg1 string, arg2 int) error {
// 使用ctx传递请求上下文
}
switch x {
case 1,2,3:
fmt.Println("ok")
}
switch x + y {
case 1:
fmt.Println("x+y=1")
case 2:
fmt.Println("x+y=2")
default:
fmt.Println("default")
}
func main() {
switch { // 等价于 switch true
case true: fmt.Println("true")
case false: fmt.Println("false")
}
}
//rand.Seed(time.Now().UnixNano()) // Go 1.20之前需要
switch n := rand.Intn(100) % 5; n {
case 0, 1, 2, 3, 4:
fmt.Println("n =", n)
fallthrough // 跳到下个case分支
case 5, 6, 7, 8:
// 一个新声明的n,它只在当前分支代码块内可见。
n := 999
fmt.Println("n =", n) // 999
fallthrough //跳到下个case分支
default:
// 这里的n和第一个分支中的n是同一个变量,它们均为switch表达式"n"。
fmt.Println("n =", n)
}
// 源码位置:$GOPATH/src/runtime/runtime2.go type _defer struct { // 参数和返回值的内存大小 siz int32 //表示该_defer语句是否已经开始执行 started bool //表示该_defer语句的优先级 //当一个_defer语句被执行时,它会被添加到_defer链表中,而heap字段则用于将_defer语句添加到一个优先队列中, //以便在函数返回时按照一定的顺序执行_defer语句。在_defer链表中,后添加的_defer语句会先被执行,而在优先队列中, //heap值较小的_defer语句会先被执行。这个字段的值是在_defer语句被添加到_defer链表时根据一定规则计算出来的, //通常是根据_defer语句的执行顺序和作用域等因素计算而得。在函数返回时,Go语言会按照heap值的大小顺序执行_defer语句。 //如果多个_defer语句的heap值相同,则它们会按照它们在_defer链表中的顺序依次执行。 //这个机制可以确保_defer语句按照一定的顺序执行,从而避免了一些潜在的问题。 heap bool // 表示该_defer用于具有开放式编码_defer的帧。开放式编码_defer是指在编译时已经确定_defer语句的数量和位置, //而不是在运行时动态添加_defer语句。在一个帧中,可能会有多个_defer语句,但只会有一个_defer结构体记录了所有_defer语句的信息, //而openDefer就是用来标识该_defer结构体是否是针对开放式编码_defer的 openDefer bool //_defer语句所在栈帧的栈指针(stack pointer) //在函数调用时,每个函数都会创建一个新的栈帧,用于保存函数的局部变量、参数和返回值等信息。 //而_defer语句也被保存在这个栈帧中,因此需要记录栈指针以便在函数返回时找到_defer语句。 //当一个_defer语句被执行时,它会被添加到_defer链表中,并记录当前栈帧的栈指针。 //在函数返回时,Go语言会遍历_defer链表,并执行其中的_defer语句。而在执行_defer语句时, //需要使用保存在_defer结构体中的栈指针来访问_defer语句所在栈帧中的局部变量和参数等信息。 //需要注意的是,由于_defer语句是在函数返回之前执行的,因此在执行_defer语句时,函数的栈帧可能已经被销毁了。 //因此,_sp字段的值不能直接使用,需要通过一些额外的处理来确保_defer语句能够正确地访问栈帧中的信息。 sp uintptr //_defer语句的程序计数器(program counter) //程序计数器是一个指针,指向正在执行的函数中的下一条指令。在_defer语句被执行时,它会被添加到_defer链表中, //并记录当前函数的程序计数器。当函数返回时,Go语言会遍历_defer链表,并执行其中的_defer语句。 //而在执行_defer语句时,需要让程序计数器指向_defer语句中的函数调用,以便正确地执行_defer语句中的代码。 //这就是为什么_defer语句需要记录程序计数器的原因。需要注意的是,由于_defer语句是在函数返回之前执行的, //因此在执行_defer语句时,程序计数器可能已经指向了其它的函数或代码块。因此,在执行_defer语句时, //需要使用保存在_defer结构体中的程序计数器来确保_defer语句中的代码能够正确地执行。 pc uintptr // pc 计数器值,程序计数器 // defer 传入的函数地址,也就是延后执行的函数 fn *funcval //defer 的 panic 结构体 _panic *_panic //用于将多个defer链接起来,形成一个defer栈 //当程序执行到一个 defer 语句时,会将该 defer 语句封装成一个 _defer 结构体,并将其插入到 defer 栈的顶部。 //当函数返回时,程序会从 defer 栈的顶部开始依次执行每个 defer 语句,直到 defer 栈为空为止。 //每个 _defer 结构体中的 link 字段指向下一个 _defer 结构体,从而将多个 _defer 结构体链接在一起。 //当程序执行完一个 defer 语句后,会将该 defer 从 defer 栈中弹出,并将其 link 字段指向的下一个 _defer 结构体设置为当前的 defer 栈顶。 //这样,当函数返回时,程序会依次执行每个 defer 语句,从而实现 defer 语句的反转执行顺序的效果。 //需要注意的是,由于 _defer 结构体是在运行时动态创建的,因此 defer 栈的大小是不固定的。 //在编写程序时,应该避免在单个函数中使用大量的 defer 语句,以免导致 defer 栈溢出。 link *_defer } func deferproc(siz int32, fn *funcval) { // arguments of fn follow fn gp := getg() //获取goroutine结构 if gp.m.curg != gp { // go code on the system stack can't defer throw("defer on system stack") } ... d := newdefer(siz) //新建一个defer结构 if d._panic != nil { throw("deferproc: d.panic != nil after newdefer") } d.link = gp._defer // 新建defer的link指针指向g的defer gp._defer = d // 新建defer放到g的defer位置,完成插入链表表头操作 d.fn = fn d.pc = callerpc d.sp = sp ... }
func main() { defer fmt.Println(1) defer fmt.Println(2) defer fmt.Println(3) } // 关闭文件句柄 func readFile(filename string) (string, error) { f, err := os.Open(filename) if err != nil { return "", err } defer f.Close() // 使用 defer 语句关闭文件 content, err := ioutil.ReadAll(f) if err != nil { return "", err } return string(content), nil } // 关闭http响应 func request() { url := "http://www.baidu.com" resp, err := http.Get(url) if err != nil { log.Fatal(err) } defer resp.Body.Close() // 关闭响应体 // 处理响应数据 } // 关闭数据库链接 func queryDatabase() error { db, err := sql.Open("mysql", "user:password@tcp(localhost:3306)/database") if err != nil { return err } defer db.Close() // 在函数返回前关闭数据库连接 // 查询数据库 return nil } var mu sync.Mutex var balance int // 释放锁 func lockRelease(amount int) { mu.Lock() defer mu.Unlock() // 在函数返回前解锁 balance += amount } // 捕获异常 func f() { defer func() { if err := recover(); err != nil { fmt.Println(string(debug.Stack())) } }() panic("unknown") } // 取消任务 func runTask() error { ctx, cancel := context.WithCancel(context.Background()) defer cancel() // 在函数返回前取消任务 go doTask(ctx) // 等待任务完成 return nil } func doTask(ctx context.Context) { select { case <-ctx.Done(): return } //do something } // 记录程序耗时 func trackTime() { start := time.Now() defer func() { log.Printf("Time took: %v", time.Since(start).Milliseconds()) }() // 执行一些操作 time.Sleep(time.Second * 3) }
func main() {
fmt.Println(f(2))
}
func f(x int) (r int) {
defer func() {
r += x // 修改返回值
}
return x+x // 等价于 r=x+x; return
}
// 最后输出结果,x=0.x的值为它被压入defer栈时x的值
func f2() {
x := 0
defer func(paramx int) {
fmt.Println(paramx)
}(x)
x = 1
fmt.Println("done")
}
// 最后输出结果x值为1,为该变量的最新的值
func f3() {
x := 0
defer func() {
fmt.Println(x)
}()
x = 1
fmt.Println("Done")
}
// f8()输出结果: // 123 // 123 // 123 // 0 type number int func (n number) print() { fmt.Println(n) } func (n *number) ptrprint() { fmt.Println(*n) } func f8() { var n number defer n.print() defer n.ptrprint() defer func() { n.print() }() defer func() { n.ptrprint() }() n = 123 }
func fn() { println("call fn") fn1() println("exit fn") } func fn1() { println("call fn1") defer func() { fmt.Println("defer before panic in fn1") }() panic("panic in fn1") defer func() { fmt.Println("defer after panic in fn1") }() fn2() println("exit fn1") } func fn2() { println("call fn2") println("exit fn2") } func main() { defer func() { if err := recover(); err != nil { fmt.Println("捕获了一个panic: ", err) fmt.Println("防止了程序崩溃") } }() println("call main") fn() println("exit main") }
func panicInDefer() {
defer func() {
fmt.Println("defer1")
panic("defer1 panic")
}()
defer func() {
fmt.Println("defer2")
panic("defer2 panic")
}()
panic("main panic")
}
defer2 defer1 panic: main panic panic: defer2 panic panic: defer1 panic goroutine 1 [running]: main.panicInDefer.func1() D:/GoProject/训练营/go_basic_interview/panic_practice.go:32 +0x65 panic({0x4b3ae0, 0x4e6810}) D:/Go/src/runtime/panic.go:838 +0x207 main.panicInDefer.func2() D:/GoProject/训练营/go_basic_interview/panic_practice.go:37 +0x65 panic({0x4b3ae0, 0x4e6820}) D:/Go/src/runtime/panic.go:838 +0x207 main.panicInDefer() D:/GoProject/训练营/go_basic_interview/panic_practice.go:40 +0x54 main.main() D:/GoProject/训练营/go_basic_interview/panic_practice.go:53 +0x17 Process finished with the exit code 2
func f() { go func() { defer func() { if err := recover(); err != nil { fmt.Println("recover", err) } }() }() panic("未知错误") } func CoveredByCurrentGoroutine() { defer func() { if err := recover(); err != nil { fmt.Println(err) } }() f() }
// 不使用panic/recover func doSomething() (err error) { isContinue, err := doStep1() if !isContinue { return err } isContinue, err = doStep2() if !isContinue { return err } isContinue, err = doStep3() if !isContinue { return err } return } func doStep1() (isContinue bool, err error) { // do something for doStep1 return } func doStep2() (isContinue bool, err error) { // do something for doStep2 return } func doStep3() (isContinue bool, err error) { // do something for doStep3 return } // 使用panic/recover func doSomething1() (err error) { defer func() { err, _ = recover().(error) }() doStep_1() doStep_2() doStep_3() return } func doStep_1() { var err error var done bool // do something for doStep1 //err = errors.New("err ") if err != nil { panic(err) } if done { panic(nil) } fmt.Println("doStep_1") } func doStep_2() { var err error var done bool // do something for doStep2 if err != nil { panic(err) } if done { panic(nil) } fmt.Println("doStep_2") } func doStep_3() { var err error var done bool // do something for doStep3 if err != nil { panic(err) } if done { panic(nil) } fmt.Println("doStep_3") }
// 源码位置:$GOPATH/src/runtime/chan.go type hchan struct { //当前队列中元素的个数。当我们向channel发送数据时,qcount会增加1;当我们从channel接收数据时,qcount会减少1 qcount uint //如果我们在创建channel时指定了缓冲区的大小,那么dataqsiz就等于指定的大小;否则,dataqsiz为0,表示该channel没有缓冲区。 dataqsiz uint //buf字段是一个unsafe.Pointer类型的指针,指向缓冲区的起始地址。如果该channel没有缓冲区,则buf为nil。 buf unsafe.Pointer //表示缓冲区中每个元素的大小。当我们创建channel时,Golang会根据元素的类型计算出elemsize的值。 elemsize uint16 // channel 是否已经关闭,当我们通过close函数关闭一个channel时,Golang会将closed字段设置为true。 closed uint32 //表示下一次接收元素的位置.当我们从channel接收数据时,Golang会从缓冲区中recvx索引的位置读取数据,并将recvx加1 recvx uint //表示下一次发送元素的位置。在channel的发送操作中,如果缓冲区未满,则会将数据写入到sendx指向的位置,并将sendx加1。 //如果缓冲区已满,则发送操作会被阻塞,直到有足够的空间可用。 sendx uint // 等待接收数据的 goroutine 队列,用于存储等待从channel中读取数据的goroutine。 //当channel中没有数据可读时,接收者goroutine会进入recvq等待队列中等待数据的到来。 //当发送者goroutine写入数据后,会将recvq等待队列中的接收者goroutine唤醒,并进行读取操作。 //在进行读取操作时,会先检查recvq等待队列是否为空,如果不为空,则会将队列中的第一个goroutine唤醒进行读取操作。 //同时,由于recvq等待队列是一个FIFO队列,因此等待时间最长的goroutine会排在队列的最前面,最先被唤醒进行读取操作。 recvq waitq // 等待发送数据的 goroutine 队列。sendq 字段是一个指向 waitq 结构体的指针,waitq 是一个用于等待队列的结构体。 //waitq 中包含了一个指向等待队列中第一个协程的指针和一个指向等待队列中最后一个协程的指针。 //当一个协程向一个 channel 中发送数据时,如果该 channel 中没有足够的缓冲区来存储数据,那么发送操作将会被阻塞, //直到有另一个协程来接收数据或者 channel 中有足够的缓冲区来存储数据。当一个协程被阻塞在发送操作时, //它将会被加入到 sendq 队列中,等待另一个协程来接收数据或者 channel 中有足够的缓冲区来存储数据。 sendq waitq //channel的读写锁,确保多个gorutine同时访问时的并发安全,保证读写操作的原子性和互斥性。 //当一个goroutine想要对channel进行读写操作时,首先需要获取lock锁。如果当前lock锁已经被其他goroutine占用, //则该goroutine会被阻塞,直到lock锁被释放。一旦该goroutine获取到lock锁,就可以进行读写操作,并且在操作完成后释放lock锁, //以便其他goroutine可以访问channel底层数据结构。 lock mutex } // 等待队列是一个包含多个 sudog 结构体的链表,用于存储正在等待发送或接收数据的 goroutine。 // 当有数据可用时,等待队列中的 goroutine 会被唤醒并继续执行。 type waitq struct { first *sudog // 队列头部指针 last *sudog // 队列尾部指针 } // sudog 结构体是一个用于等待队列中的 goroutine 的结构体, // 它包含了等待的 goroutine 的信息,如等待的 channel、等待的元素值、 // 等待的方向(发送或接收)等。 type sudog struct { // 等待的 goroutine g *g // 指向下一个 sudog 结构体 next *sudog // 指向上一个 sudog 结构体 prev *sudog //等待队列的元素 elem unsafe.Pointer // 获取锁的时间 acquiretime int64 // 释放锁的时间 releasetime int64 //用于实现自旋锁。当一个gorutine需要等待另一个gorutine操作完成, //而等待时间很短的情况下就会使用自旋锁。 //它会先获取当前的ticket值,并将其加1。然后,它会不断地检查结构体中的ticket字段是否等于自己的ticket值, //如果相等就说明获取到了锁,否则就继续自旋等待。当锁被释放时,另一个goroutine会将ticket值加1,从而唤醒等待的goroutine。 //需要注意的是,自旋锁适用于等待时间很短的场景,如果等待时间较长,就会造成CPU资源的浪费 ticket uint32 // 等待的 goroutine是否已经被唤醒 isSelect bool //success 表示通道 c 上的通信是否成功。 //如果 goroutine 是因为在通道 c 上接收到一个值而被唤醒,那么 success 为 true; //如果是因为通道 c 被关闭而被唤醒,那么 success 为 false。 success bool //用于实现gorutine的堆栈转移 //当一个 goroutine 调用另一个 goroutine 时,它会创建一个 sudog 结构体,并将自己的栈信息保存在 sudog 结构体的 parent 字段中。 //然后,它会将 sudog 结构体加入到等待队列中,并等待被调用的 goroutine 执行完成。 //当被调用的 goroutine 执行完成时,它会将 sudog 结构体从等待队列中移除,并将 parent 字段中保存的栈信息恢复到调用者的栈空间中。 //这样,调用者就可以继续执行自己的任务了。 //需要注意的是,sudog 结构体中的 parent 字段只在 goroutine 调用其他 goroutine 的时候才会被使用, //因此在普通的 goroutine 执行过程中,它是没有被使用的。 parent *sudog // semaRoot binary tree //用于连接下一个等待的 sudog 结构体 //等待队列是一个链表结构,每个 sudog 结构体都有一个 waitlink 字段,用于连接下一个等待的 sudog 结构体。 //当被等待的 goroutine 执行完成时,它会从等待队列中移除对应的 sudog 结构体, //并将 sudog 结构体中的 waitlink 字段设置为 nil,从而将其从等待队列中移除。 //需要注意的是,waitlink 字段只有在 sudog 结构体被加入到等待队列中时才会被使用。 //在普通的 goroutine 执行过程中,waitlink 字段是没有被使用的。 waitlink *sudog // g.waiting list or semaRoot //等待队列的尾部指针,waittail 字段指向等待队列的尾部 sudog 结构体。 //当被等待的 goroutine 执行完成时,它会从等待队列中移除对应的 sudog 结构体,并将 sudog 结构体中的 waitlink 字段设置为 nil, //从而将其从等待队列中移除。同时,waittail 字段也会被更新为等待队列的新尾部。 //需要注意的是,waittail 字段只有在 sudog 结构体被加入到等待队列中时才会被使用。 //在普通的 goroutine 执行过程中,waittail 字段是没有被使用的。 waittail *sudog // semaRoot //在golang中,goroutine是轻量级线程,其调度由golang运行时系统负责。当一个goroutine需要等待某些事件的发生时, //它可以通过阻塞等待的方式让出CPU资源,等待事件发生后再被唤醒继续执行。这种阻塞等待的机制是通过wait channel实现的。 //在sudog结构体中,c字段指向的wait channel是一个用于等待某些事件发生的channel。 //当一个goroutine需要等待某些事件时,它会创建一个sudog结构体,并将该结构体中的c字段指向wait channel。 //然后,它会将该sudog结构体加入到wait channel的等待队列中,等待事件发生后再被唤醒继续执行。 //当一个goroutine需要等待某些事件时,它会将自己加入到wait channel的等待队列中,并阻塞等待事件发生。 //当事件发生后,wait channel会将等待队列中的goroutine全部唤醒,让它们继续执行。 //这种机制可以有效地避免busy waiting,提高CPU利用率。 c *hchan // channel } type lockRankStruct struct { } // Mutual exclusion locks. In the uncontended case, // as fast as spin locks (just a few user-level instructions), // but on the contention path they sleep in the kernel. // A zeroed Mutex is unlocked (no need to initialize each lock). // Initialization is helpful for static lock ranking, but not required. type mutex struct { // Empty struct-demo if lock ranking is disabled, otherwise includes the lock rank lockRankStruct // Futex-based impl treats it as uint32 key, // while sema-based impl as M* waitm. // Used to be a union, but unions break precise GC. key uintptr } // Needs to be in sync with ../cmd/link/internal/ld/decodesym.go:/^func.commonsize, // ../cmd/compile/internal/reflectdata/reflect.go:/^func.dcommontype and // ../reflect/type.go:/^type.rtype. // ../internal/reflectlite/type.go:/^type.rtype. type _type struct { size uintptr ptrdata uintptr // size of memory prefix holding all pointers hash uint32 tflag tflag align uint8 fieldAlign uint8 kind uint8 // function for comparing objects of this type // (ptr to object A, ptr to object B) -> ==? equal func(unsafe.Pointer, unsafe.Pointer) bool // gcdata stores the GC type data for the garbage collector. // If the KindGCProg bit is set in kind, gcdata is a GC program. // Otherwise it is a ptrmask bitmap. See mbitmap.go for details. gcdata *byte str nameOff ptrToThis typeOff } type g struct { // goroutine 的栈信息 stack stack stackguard0 uintptr // offset known to liblink stackguard1 uintptr // offset known to liblink _panic *_panic // innermost panic - offset known to liblink _defer *_defer // innermost defer m *m // current m; offset known to arm liblink // goroutine 调度器上下文信息 sched gobuf syscallsp uintptr // if status==Gsyscall, syscallsp = sched.sp to use during gc syscallpc uintptr // if status==Gsyscall, syscallpc = sched.pc to use during gc stktopsp uintptr // expected sp at top of stack, to check in traceback // param is a generic pointer parameter field used to pass // values in particular contexts where other storage for the // parameter would be difficult to find. It is currently used // in three ways: // 1. When a channel operation wakes up a blocked goroutine, it sets param to // point to the sudog of the completed blocking operation. // 2. By gcAssistAlloc1 to signal back to its caller that the goroutine completed // the GC cycle. It is unsafe to do so in any other way, because the goroutine's // stack may have moved in the meantime. // 3. By debugCallWrap to pass parameters to a new goroutine because allocating a // closure in the runtime is forbidden. param unsafe.Pointer // 原子级别的 goroutine 运行状态 atomicstatus atomic.Uint32 stackLock uint32 // sigprof/scang lock; TODO: fold in to atomicstatus goid uint64 schedlink guintptr waitsince int64 // approx time when the g become blocked // goroutine 等待的原因 waitreason waitReason // if status==Gwaiting preempt bool // preemption signal, duplicates stackguard0 = stackpreempt preemptStop bool // transition to _Gpreempted on preemption; otherwise, just deschedule preemptShrink bool // shrink stack at synchronous safe point // asyncSafePoint is set if g is stopped at an asynchronous // safe point. This means there are frames on the stack // without precise pointer information. asyncSafePoint bool paniconfault bool // panic (instead of crash) on unexpected fault address gcscandone bool // g has scanned stack; protected by _Gscan bit in status throwsplit bool // must not split stack // activeStackChans indicates that there are unlocked channels // pointing into this goroutine's stack. If true, stack // copying needs to acquire channel locks to protect these // areas of the stack. activeStackChans bool // parkingOnChan indicates that the goroutine is about to // park on a chansend or chanrecv. Used to signal an unsafe point // for stack shrinking. parkingOnChan atomic.Bool raceignore int8 // ignore race detection events sysblocktraced bool // StartTrace has emitted EvGoInSyscall about this goroutine tracking bool // whether we're tracking this G for sched latency statistics trackingSeq uint8 // used to decide whether to track this G trackingStamp int64 // timestamp of when the G last started being tracked runnableTime int64 // the amount of time spent runnable, cleared when running, only used when tracking sysexitticks int64 // cputicks when syscall has returned (for tracing) traceseq uint64 // trace event sequencer tracelastp puintptr // last P emitted an event for this goroutine lockedm muintptr // goroutine 等待的信号 sig uint32 writebuf []byte sigcode0 uintptr sigcode1 uintptr sigpc uintptr gopc uintptr // pc of go statement that created this goroutine ancestors *[]ancestorInfo // ancestor information goroutine(s) that created this goroutine (only used if debug.tracebackancestors) startpc uintptr // pc of goroutine function racectx uintptr waiting *sudog // sudog structures this g is waiting on (that have a valid elem ptr); in lock order cgoCtxt []uintptr // cgo traceback context labels unsafe.Pointer // profiler labels timer *timer // cached timer for time.Sleep selectDone atomic.Uint32 // are we participating in a select and did someone win the race? // goroutineProfiled indicates the status of this goroutine's stack for the // current in-progress goroutine profile goroutineProfiled goroutineProfileStateHolder // Per-G GC state // gcAssistBytes is this G's GC assist credit in terms of // bytes allocated. If this is positive, then the G has credit // to allocate gcAssistBytes bytes without assisting. If this // is negative, then the G must correct this by performing // scan work. We track this in bytes to make it fast to update // and check for debt in the malloc hot path. The assist ratio // determines how this corresponds to scan work debt. gcAssistBytes int64 }
readOnlyChanWithBuff := make(<-chan int, 2) // 只读且带缓冲
readOnlyChan := make(<-chan int) // 只读且不带缓冲
wirteOnlyChanWithBuff := make(chan <- int, 4) // 只写且带缓冲
wirteOnlyChan := make(chan <- int) // 只写且不带缓冲
ch := make(chan int, 10) // 可读可写且带缓冲
func f14() { ch := make(chan int, 2) ch1 := make(chan int, 2) go func() { for { select { case <-ch: ch1 <- 1 } } }() for { select { case <-ch1: ch <- 1 } } }
func f1() { ch := make(chan int) close(ch) go func() { fmt.Println("开始接收数据:") fmt.Println("<-ch:", <-ch) fmt.Println("数据接收完成") }() go func() { fmt.Println("开始发送数据") ch <- 1 fmt.Println("发送数据完毕") }() time.Sleep(time.Second) }
func f2() { ch := make(chan int) close(ch) for { select { case c := <-ch: fmt.Println("读取<-ch: ", c) time.Sleep(time.Second) } } } func f3() { ch := make(chan int) close(ch) <-ch println("完成") // 如果这句话会输出则说明不会阻塞 }
// 多次关掉 channel 会触发运行时错误
func f7() {
c := make(chan int)
defer close(c)
go func() {
c <- 1
close(c)
}()
fmt.Println(<-c)
}
type Duck interface { Quack() } type YellowDuck struct { } func (yd YellowDuck) Quack() { fmt.Println("葛诗颖 葛诗颖") } type NormalDuck struct { } func (nd NormalDuck) Quack() { fmt.Println("诗诗 诗诗 诗诗") } func Quack(d Duck) { d.Quack() } func main() { yd := YellowDuck{} nd := NormalDuck{} Quack(yd) Quack(nd) }
type Animal struct { } func (a *Animal) eat() { fmt.Println("葛诗颖") } // Cat继承Animal type Cat struct { Animal } // Cat子类也可以有eat方法,且实现可以跟父类Animal不同 func (c *Cat) eat() { fmt.Println("诗诗") } func main() { a := &Animal{} c := &Cat{} a.eat() c.eat() }
type Iperson interface { Talk() Walk() } type Person struct { Iperson } func (p *Person) Talk() { fmt.Println("talk") } //func (p *Person) Walk() { // fmt.Println("walk") //} func Talk(p Iperson) { p.Talk() } type Teacher struct { } func (t *Teacher) Talk() { fmt.Println("Teacher talk") } func (t *Teacher) Walk() { fmt.Println("Teacher walk") } func main() { p := &Person{} var i Iperson i = p fmt.Println(i) p.Talk() p.Walk() t := &Teacher{} Talk(p) Talk(t) }
type People struct{} func (p *People) Run() { fmt.Println("Run") p.Eat() } func (p *People) Eat() { fmt.Println("Eat") } type Person struct{} func (p *Person) Run() { fmt.Println("Person Run") p.Eat() } func (p *Person) Eat() { fmt.Println("Person Eat") } type Teacher struct { //People *People *Person } func (t *Teacher) Speck() { fmt.Println("teacher Speck") } func (t *Teacher) Run() { fmt.Println("teacher Eat") } func (t *Teacher) Eat() { fmt.Println("teacher Eat") } func main() { t := Teacher{} t.Run() t.Eat() t.Speck() }
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。