• [go]并发编程(go关键字和select关键字)


    go关键字 - 协程(异步)管理结构

    其实和if switch for等循环结构一样. go 是一种控制结构, 控制协程的启动等生命周期.

    并发基础

    程序与进程
    并发与并行

    启动多个协程

    // 同步代码
    func hello() {
        time.Sleep(time.Second/3)
    	fmt.Println("Hello Goroutine!")
    }
    func main() {
    	hello()
    	fmt.Println("main goroutine done!")
    }
    
    //异步
    func hello() {
        time.Sleep(time.Second/3)
    	fmt.Println("Hello Goroutine!")
    }
    func main() {
    	go hello()                          // 启动另外一个goroutine去执行hello函数
    	fmt.Println("main goroutine done!") //不会被输出
    }
    
    //main结束,程序即运行结束
    func main() {
    	go hello() // 启动另外一个goroutine去执行hello函数
    	fmt.Println("main goroutine done!")
        time.Sleep(time.Second)
    }
    
    • 协程同步
    - runtime包里的函数
    
    // runtime.GOMAXPROCS() 设置核心数
    
    - 用一个core执行
    func main() {
    	runtime.GOMAXPROCS(1)
    	go func() {
    		for {
    			fmt.Print(1)
    
    		}
    	}()
    	go func() {
    		for {
    			fmt.Print(2)
    		}
    	}()
    	time.Sleep(time.Second * 1)
    }
    //打印的1 2 成块的
    
    
    - 用2个core执行
    func main() {
    	runtime.GOMAXPROCS(2)
    	go func() {
    		for {
    			fmt.Print(1)
    
    		}
    	}()
    	go func() {
    		for {
    			fmt.Print(2)
    		}
    	}()
    	time.Sleep(time.Second * 1)
    }
    //打印的1 2交替较明显
    
    // runtime.Gosched() //出让当前执行机会, 等下次又机会时继续往下执行
    func main() {
    	runtime.GOMAXPROCS(1)
    	go func() {
    		for {
    			fmt.Print("m")
    
    		}
    	}()
    	go func() {
    		for {
    		    runtime.Gosched()
    			fmt.Print("g")
    		}
    	}()
    	time.Sleep(time.Second)
    }
    
    - runtime.Goexit()结束本go程
    - return结束当前函数
    
    // return结束当前函数
    func test() {
    	println(1)
    	return
    	println(2)
    }
    
    func main() {
    	test()
    	println("main")
    }
    //1
    //main
    
    //runtime.Goexit结束当前go程
    func test() {
    	println(1)
    	runtime.Goexit()
    	println(2)
    }
    
    func main() {
    	test()
    	println("main")
    }
    // 1
    // fatal error: no goroutines (main called runtime.Goexit) - deadlock!
    
    // defer: 当函数结束时被调用
    
    func main() {
    	defer println("test end 1") //栈结构:一个桶 后进先出
    	defer println("test end 2")
    	println("testing")
    }
    //testing
    //test end 2
    //test end 1
    
    // return后的defer不会被注册
    func main() {
    	defer println("1")
    	println("main")
    	return
    	defer println("2")
    }
    //main
    //1
    
    // runtime.Goexit后的defer不会被注册
    func main() {
    	defer println("1")
    	println("main")
    	runtime.Goexit()
    	defer println("2")
    }
    //main
    //1
    //fatal error: no goroutines (main called runtime.Goexit) - deadlock!
    
    - 小结: 
        runtime.Goexit()和return后的defer都不会被注册, 
        只有代码能执行到,defer才会被注册
    
    
    func main() {
        go func() {
            defer fmt.Println("aaaaaaaaaaaaa")
            runtime.Goexit()
            defer fmt.Println("bbbbbbbbbb")
        }()
        time.Sleep(time.Second * 1)
    }
    //aaaaaaaaaaaaa
    
    
    func main() {
        go func() {
            defer fmt.Println("aaaaaaaaaaaaa")
            return
            defer fmt.Println("bbbbbbbbbb")
        }()
        time.Sleep(time.Second * 1)
    }
    //aaaaaaaaaaaaa
    
    // 带go
    func test() {
        defer fmt.Println("ccccccccccc")
        runtime.Goexit()
        defer fmt.Println("dddddddddddddd")
    }
    
    func main() {
        go func() {
            defer fmt.Println("aaaaaaaaaaaaa")
            go test()
            defer fmt.Println("bbbbbbbbbb")
        }()
        time.Sleep(time.Second)
    }
    //bbbbbbbbbb
    //aaaaaaaaaaaaa
    //ccccccccccc
    
    //不带go
    func test() {
        defer fmt.Println("ccccccccccc")
        runtime.Goexit()
        defer fmt.Println("dddddddddddddd")
    }
    
    func main() {
        go func() {
            defer fmt.Println("aaaaaaaaaaaaa")
            test()
            defer fmt.Println("bbbbbbbbbb")
        }()
        time.Sleep(time.Second)
    }
    //ccccccccccc
    //aaaaaaaaaaaaa
    

    定时器

    [go]time包

    睡一秒

    //方法1:
    func main() {
        time.Sleep(time.Second)
    }
    
    //方法2:
    func main() {
    	fmt.Println(<-time.NewTimer(time.Second).C))
    }
    
    //方法3:
    func main() {
        fmt.Println(<-time.After(time.Second))
    }
    
    
    // 定时回调(setTimeout)
    time.AfterFunc(time.Second, func() {
        println("after 1s cb")
    })
    time.Sleep(time.Second*3)
    
    //周期定时器(setInterval)
    func main() {
    	tickTimer := time.NewTicker(time.Second)
    	for {
    		fmt.Println(<-tickTimer.C)
    	}
    }
    

    select - 管理多个chan

    select是一种控制结构, 和if switch for控制结构一样, 用于控制管理多个管道

    Go并发编程—select的使用

    select可以同时监控多个通道的情况,只处理未阻塞的case。
    每个case必须是一个chan的io操作
    
    select会阻塞在多个channel上,对多个channel的读/写事件进行监控。
    case中的channel的事件包括:
        读取的时候,
        channel被close,或
        写入时channel没有空间。
    
    // 多个chan,随机选一个执行. 对多个chanel调度机会基本是均等的
    
    当通道为nil时,对应的case永远为阻塞,无论读写。
        特殊关注:普通情况下,对nil的通道写操作是要panic的。
    
    func main() {
        c1 := make(chan interface{}); close(c1)
        c2 := make(chan interface{}); close(c2)
        c3 := make(chan interface{}); close(c3)
    
        var c1Count, c2Count, c3Count int
        for i := 1000; i >= 0; i-- {
            select {
            case <-c1:
                c1Count++
            case <-c2:
                c2Count++
            case <-c3:
                c3Count++
            }
        }
        fmt.Printf("c1Count: %d
    c2Count: %d
    c3Count: %d
    ", c1Count, c2Count, c3Count)
    }
    
    //select管理多个管道
    func main() {
    	ch := make(chan int, 1) //仅限于缓冲通道
    	for i := 0; i < 10; i++ {
    		select {
    		case x := <-ch:
    			fmt.Println(x)
    		case ch <- i:
    		}
    	}
    }
    
    // select管理多个管道
    func main() {
    	ch := make(chan int)
    	done := make(chan bool)
    	go func() {
    		for i := 0; i < 10; i++ {
    			ch <- i
    			time.Sleep(time.Second / 3)
    		}
    		close(ch)
    		done <- true
    	}()
    	for {
    		select {
    		case <-done:
    			fmt.Println("Done!")
    			return
    		case t := <-ch:
    			fmt.Println("ch value: ", t)
    		}
    	}
    }
    
    
    
    
    
    // 没有任何channel准备好,处理默认事件
    func main() {
    	start := time.Now()
    	var c1, c2 <-chan int
    	select {
    	case <-c1:
    	case <-c2:
    
    	default:
    		fmt.Printf("In default after %v
    
    ", time.Since(start))
    	}
    }
    
    
    // 没有任何channel准备好,处理超时
    
    func main() {
        var c <-chan int
    
        for {
            select {
            case <-c:
            case <-time.After(1 * time.Second):
                fmt.Println("Timed out.Do something.")
            }
        }
    }
    
    2. 退出
    
    select {
        case <- done:
            cleanUp()
            return
        default:
    }
    
    3.判断channel是否阻塞
    func main() {
    	var ch chan int = make(chan int, 5)
    	for {
    		select {
    		case ch <- 1:
    			fmt.Println("add success")
    		default: // channel满了
    			fmt.Println("chan is full")
                return
    		}
    	}
    }
    
    4.检测chanel关闭事件
    func main() {
        start := time.Now()
        c := make(chan interface{})
    
        go func() {
            time.Sleep(2*time.Second)
            close(c)
        }()
    
        fmt.Println("Blocking on read...")
        select {
        case <-c:
            fmt.Printf("Unblocked %v later.
    ", time.Since(start))
        }
    }
    
    
    // select{} 永久阻塞, 直到有信号中断.
    func main() {
    	go func() {
    		for {
    			fmt.Println(time.Now())
    			time.Sleep(time.Second / 3)
    		}
    	}()
    
    	select {}
    }
    
  • 相关阅读:
    C++test 关于resource参数和include/exclude参数说明
    开发程序 ip 127.0.0.0 0.0.0.0原理与区别
    安装rails旧版本出错bin_path': can't find gem railties (["3.0.5"]) with executable rails (Gem::GemNotFoundException)
    rails excel的创建
    ruby nil? empty? blank? 的区别
    rails 调试工具pry 换掉debugger 和 rails c
    rails respond_to 的原理与使用
    rails transaction 的用法
    ssh的传送文件命令
    出现了pid的错误A server is already running. 和如何改变webrick的端口值
  • 原文地址:https://www.cnblogs.com/iiiiiher/p/12251519.html
Copyright © 2020-2023  润新知