• golang从实践到放弃 牧羊人


    golang是世界上最好的语言【呸!啥也不是】

    开发环境配置

    设置环境变量

    如果vs code安装插件下载失败,配置一下go环境变量,设置proxy

    go env -w GOPROXY=https://goproxy.io,direct
    go env -w GO111MODULE=on
    

    查看go环境变量

    go env
    

    所有go的环境变量

    set GO111MODULE=on
    set GOARCH=amd64
    set GOPATH=C:\Users\Administrator\go
    set GOPRIVATE=
    set GOPROXY=https://goproxy.io,direct
    set GOROOT=c:\go
    set GOSUMDB=sum.golang.org
    ……
    

    第一个go web程序

    新建main.go

    输入helloweb按回车

    package main
    
    import (
    	"fmt"
    	"net/http"
    	"time"
    )
    
    func greet(w http.ResponseWriter, r *http.Request) {
    	fmt.Fprintf(w, "Hello World! %s", time.Now())
    }
    
    func main() {
    	http.HandleFunc("/", greet)
    	http.ListenAndServe(":8080", nil)
    }
    

    命令行执行go run main.go

    然后命令行访问服务

    curl http://localhost:8080
    

    得到以下的结果,那么第一个golang web就执行成功了

    StatusCode        : 200
    StatusDescription : OK
    Content           : Hello World! 2022-02-09 20:25:07.5051433 +0800 CST m=+55.242234801
    

    restful接口定义

    导入依赖

    go mod init  example/web-service-gin
    

    产生模块管理文件

    module example/web-service-gin
    go 1.15
    

    main.go导入github.com/gin-gonic/gin

    import (
      "net/http"
      "github.com/gin-gonic/gin"
    )
    

    执行

    go get .
    

    go.mod(类似nodejs的package.json)

    module example/web-service-gin
    go 1.15
    require github.com/gin-gonic/gin v1.7.7
    

    自动扫码导入依赖同时会发现目录产生了go.sum文件,其类似nodejs的package.json.lock文件

    github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
    github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
    github.com/gin-contrib/sse v0.1.0 h1:Y/yl/+YNO8GZSjAhjMsSuLt29uWRFHdHYUb5lYOV9qE=
    github.com/gin-contrib/sse v0.1.0/go.mod h1:RHrZQHXnP2xjPF+u1gW/2HnVO7nvIa9PG3Gm+fLHvGI=
    github.com/gin-gonic/gin v1.7.7 h1:3DoBmSbJbZAWqXJC3SLjAPfutPJJRN1U5pALB7EeTTs=
    github.com/gin-gonic/gin v1.7.7/go.mod h1:axIBovoeJpVj8S3BwE0uPMTeReE4+AfFtqpqaZ1qq1U=
    github.com/go-playground/assert/v2 v2.0.1/go.mod h1:VDjEfimB/XKnb+ZQfWdccd7VUvScMdVu0Titje2rxJ4=
    ……
    

    编写代码

    package main
    
    import (
    	"net/http"
    	"github.com/gin-gonic/gin"
    )
    
    type album struct {
    	ID     string  `json:"id"`
    	Title  string  `json:"title"`
    	Artist string  `json:"artist"`
    	Price  float64 `json:"price"`
    }
    
    var albums = []album{
    	{ID: "1", Title: "Blue Train", Artist: "John Coltrane", Price: 56.99},
    	{ID: "2", Title: "Jeru", Artist: "Gerry Mulligan", Price: 17.99},
    	{ID: "3", Title: "Sarah Vaughan and Clifford Brown", Artist: "Sarah Vaughan", Price: 39.99},
    }
    
    func getAlbums(ctx *gin.Context) {
        //返回带缩减的JSON数据
    	ctx.IndentedJSON(http.StatusOK, albums)
    }
    
    func main() {
    	router := gin.Default()
        router.GET("/albums", getAlbums)//method:get
    	router.POST("/albums", getAlbums)//method:get
    	router.Run("localhost:8080")
    }
    

    拆分文件

    把GetAlbums放到controller,新建controller目录,这也是package的名称,新建albumController.go文件

    package controller
    
    import (
    	"example/web-service-gin/models"
    	"net/http"
    
    	"github.com/gin-gonic/gin"
    )
    
    func GetAlbums(ctx *gin.Context) {
    	albums := []models.Album{
    		{ID: "1", Title: "Blue Train", Artist: "John Coltrane", Price: 56.99},
    		{ID: "2", Title: "Jeru", Artist: "Gerry Mulligan", Price: 17.99},
    		{ID: "3", Title: "Sarah Vaughan and Clifford Brown", Artist: "Sarah Vaughan", Price: 39.99},
    	}
    	ctx.IndentedJSON(http.StatusOK, albums)
    }
    

    把Albums结构体独立到models的album.go,go导出方法和结构,都是通过首字母大写的是公开的,其他的不导出,所以album改成Album

    package models
    
    type Album struct {
    	ID     string  `json:"id"`
    	Title  string  `json:"title"`
    	Artist string  `json:"artist"`
    	Price  float64 `json:"price"`
    }
    

    此时main.go就变成了这样子

    package main
    
    import (
    	"example/web-service-gin/controller"
    	"github.com/gin-gonic/gin"
    )
    
    func main() {
    	router := gin.Default()
    	router.GET("/albums", controller.GetAlbums)
    	router.POST("/albums", controller.GetAlbums)
    	router.Run("localhost:8080")
    }
    

    单元测试

    安装依赖

    golang的版本有要求,go install 1.15.x的版本没有对go install 的支持,建议升级到最新的版本

    go install golang.org/dl/go1.18beta1@latest
    

    单应测试样例

    创建test文件夹,创建api_test.go,注意名称要以_test结尾

    package test
    
    import (
    	"bytes"
    	"encoding/json"
    	"fmt"
    	"io/ioutil"
    	"net/http"
    	"testing"
    )
    
    func TestApi(t *testing.T) {
    	res, err := http.Get("http://localhost:8080/albums")
    	if err != nil {
    		panic(err)
    	}
    	//defer 在作用域内最后执行
    	defer res.Body.Close()
    	result, _ := ioutil.ReadAll(res.Body)
    	fmt.Println(string(result))
    }
    
    func TestPost(t *testing.T) {
    	data, _ := json.Marshal(struct{ Name, Age string }{})
    	res, err := http.Post("http://localhost:8080/albums", "application/json", bytes.NewBuffer(data))
    	if err != nil {
    		panic(err)
    	}
        
    	defer res.Body.Close()
    	result, _ := ioutil.ReadAll(res.Body)
    	fmt.Println(string(result))
    }
    

    mysql访问

    package db
    
    import (
    	"database/sql"
    	"example/web-service-gin/models"
    	"fmt"
    	"log"
    	"github.com/go-sql-driver/mysql"
    )
    
    func QueryData () (albumArr []models.Album,queryErr error){
    	var db *sql.DB;
    	cft :=mysql.Config{
    		User:"root",
    		Passwd: "123456",
            Net:    "tcp",
            Addr:   "127.0.0.1:3306",
            DBName: "recodings",
    		AllowNativePasswords:true,
    	}
        db, err := sql.Open("mysql", cft.FormatDSN())
        if err != nil {
            log.Fatal(err)
        }
    
    	pingErr := db.Ping()
        if pingErr != nil {
            log.Fatal(pingErr)
        }
        fmt.Println("Connected!")
    
    	rows,dbErr:= db.Query("select * from album");
    	if(dbErr!=nil){
    
    	}
    	defer rows.Close()
    	var albums []models.Album
    	for rows.Next() {
    		var alb models.Album 
            if err := rows.Scan(&alb.ID, &alb.Title, &alb.Artist, &alb.Price); err != nil {
                return nil, nil
            }
            albums = append(albums, alb)
    	}
    	return albums,nil
    }
    

    单元测试

    package test
    
    import (
    	"encoding/json"
    	"example/web-service-gin/db"
    	"fmt"
    	"testing"
    )
    
    func TestDB(t *testing.T) {
    	albums, err := db.QueryData()
    	if err != nil {
    		fmt.Println(err)
    	}
    	data, _ := json.MarshalIndent(albums, "", "	")
    	fmt.Println(string(data))
    }
    

    得到下面的结果

    === RUN   TestDB
    Connected!
    [
            {
                    "id": "1",
                    "title": "Blue Train",
                    "artist": "John Coltrane",
                    "price": 56.99
            },
            {
                    "id": "2",
                    "title": "Giant Steps",
                    "artist": "John Coltrane",
                    "price": 63.99
            },
            {
                    "id": "3",
                    "title": "Jeru",
                    "artist": "Gerry Mulligan",
                    "price": 17.99
            },
            {
                    "id": "4",
                    "title": "Sarah Vaughan",
                    "artist": "Sarah Vaughan",
                    "price": 34.98
            }
    ]
    --- PASS: TestDB (0.00s)
    PASS
    ok      example/web-service-gin/test    (cached)
    

    接口

    接口定义

    package interfacedemo
    
    import "example/web-service-gin/models"
    
    type IAlbumService interface {
    	GetAlbum() []models.Album
    }
    

    实现接口,AlbumService在代码上,不需要引入IAlbumService,只要AlbumService实现了所有的方法即代表AlbumService实现了接口IAlbumService(目前来看,接口没有字段的定义),如果没实现所有的方法编译器将会提示declaration: missing method GetAlbum

    package interfacedemo
    
    import "example/web-service-gin/models"
    
    type AlbumService struct {
    
    }
    
    func (service *AlbumService) GetAlbum() []models.Album {
    	return []models.Album{{ID: "10", Title: "Blue Train", Artist: "John Coltrane", Price: 100.99}}
    }
    

    调用

    package test
    
    import (
    	"encoding/json"
    	"example/web-service-gin/interfacedemo"
    	"example/web-service-gin/models"
    	"fmt"
    	"testing"
    )
    
    func TestInterface(t *testing.T) {
        //接口接收实现的实例
    	var service interfacedemo.IAlbumService =new(interfacedemo.AlbumService)
    
    	var albums []models.Album = service.GetAlbum()
    	data, _ := json.MarshalIndent(albums, "", "	")
    	fmt.Println("data:", string(data))
    }
    

    指针

    &取址操作,*声明指针类型的变量或者取指针变量指向的值

    num := 10
    //*声明指针的变量
    var numPtr *int = nil
    //&取num变量的地址
    numPtr = &num
    //*取指针变量numPtr指向的值
    ptrValue := *numPtr
    

    指针是强类型的,不匹配的类型不能赋值,比如下面的语句就是错误的

    var floatPtr *float32=numPtr
    

    不过对于struct的赋值,具有隐式转换,比如下面的例子

    type IPointer interface{GetPoint()}
    
    type Pointer struct {}
    func (pointer Pointer) GetPoint() {}
    
    func main() {
        var pt *Pointer = new(Pointer)
    	var pt1 IPointer = *point
    	var pt2 IPointer = point
        
        var pointer Pointer = Pointer{}
    	var pointer1 IPointer = &pointer
    	var pointer2 IPointer = pointer
    }	
    

    如果是结构体本身,那么不存在隐式转换

    var point *Pointer = new(Pointer)
    var point1 Pointer = *point//正确
    var point2 *Pointer = point//正确
    var point3 Pointer = point//错误
    

    再看下面的例子

    package interfacedemo
    
    import "example/web-service-gin/models"
    
    type IAlbumService interface {
    	GetAlbum() []models.Album
    }
    

    AlbumService和AlbumServiceImpl实现了接口IAlbumService

    package interfacedemo
    
    import "example/web-service-gin/models"
    
    type AlbumService struct {}
    // 通过AlbumService实现方法GetAlbum,通过该方式实现,指针(隐式转换)和实例均可给接口进行赋值
    func (service AlbumService) GetAlbum() []models.Album {
    	return []models.Album{{ID: "10", Title: "Blue Train", Artist: "John Coltrane", Price: 100.99}}
    }
    
    type AlbumServiceImpl struct{}
    // 通过AlbumServiceImpl的指针类型实现GetAlbum,只能通过指针给接口进行赋值
    func (service *AlbumServiceImpl) GetAlbum() []models.Album {
    	return []models.Album{{ID: "10", Title: "Blue Train Impl", Artist: "John Coltrane", Price: 100.99}}
    }
    

    通过以下的方式调用

    var service *interfacedemo.AlbumService = new(interfacedemo.AlbumService)
    var service1 interfacedemo.IAlbumService = service//正确,隐式转换
    service1 = *service//正确,AlbumService实例实现了GetAlbum,比较规范的写法
    
    var instance interfacedemo.AlbumService = interfacedemo.AlbumService{}
    var instance1 interfacedemo.IAlbumService = service//正确,AlbumService实例实现了GetAlbum,比较规范的写法
    instance1 = &instance//正确,隐式转换
    
    var serviceImpl interfacedemo.AlbumServiceImpl = interfacedemo.AlbumServiceImpl{}
    var serviceImpl1 interfacedemo.IAlbumService = &serviceImpl//正确
    	serviceImpl1 = serviceImpl//错误,AlbumServiceImpl的实例并没有实现GetAlbum
    
    var implInstance *interfacedemo.AlbumServiceImpl = new(interfacedemo.AlbumServiceImpl)
    var implInstance1 interfacedemo.IAlbumService = implInstance//正确
    	implInstance1 = *implInstance//错误,AlbumServiceImpl的实例并没有实现GetAlbum
    

    接口本身声明指针类型是可以的,除了赋值为nil,没办法实例化

    var service *interfacedemo.IAlbumService =nil//正确
        service = new(interfacedemo.AlbumService)//错误
    

    会抛异常:cannot use new(interfacedemo.AlbumService) (value of type *interfacedemo.AlbumService) as *interfacedemo.IAlbumService value in assignment

    golang的指针类型跟C++的有所不同

    type Pointer struct {}
    func main() {
    	var instance Pointer = Pointer{}
    	fmt.Println(instance,instancePtr,&instancePtr)
    }
    

    得到的结果是

    {} &{} 0xc000006028
    

    &instance并不是得到一个地址,更像一个表示对instance取址的操作,&instancePtr则是instancePtr的地址,也就是指针的指针

    那如果输出修改成

    fmt.Println(instance,&instance,&(&instance))
    

    &(&instance)编译器报错:invalid operation: cannot take address of (&instance) (value of type *Pointer)

    &instancePtr与&(&instance)不相同,&(&instance)这样并没有给指向&instance分配地址

    值得注意的是golang的结构体是值类型的,如果需要修改结构体的字段的值,需要用指针,下面来看一下这个例子

    package main
    
    import (
    	"fmt"
    )
    
    type Vertex struct {
    	X, Y int
    }
    
    func changeVertex(ver Vertex) {
    	ver.X = 100
    	ver.Y = 100
    }
    
    func changeVertexByPointer(ver *Vertex) {
        (*ver).X = 100//*号优先级是低于.的,所以要加上(),当然,直接ver.X也是可以的
    	ver.Y = 100
    }
    
    func main() {
    	ver := Vertex{X: 10, Y: 10}
    	changeVertex(ver)
    	fmt.Println(ver.X, ver.Y)
    
    	changeVertexByPointer(&ver)
    	fmt.Println(ver.X, ver.Y)
    }
    

    得到的结果是

    10 10
    100 100
    

    换一种写法

    package main
    
    import (
    	"fmt"
    )
    
    type Vertex struct {
    	X, Y int
    }
    
    func (ver Vertex) changeVertex() {
    	ver.X = 100
    	ver.Y = 100
    }
    
    func (ver *Vertex) changeVertexByPointer() {
    	(*ver).X = 100
    	ver.Y = 100
    }
    
    func main() {
        //实例调用
    	ver := Vertex{X: 10, Y: 10}
    	ver.changeVertex()
    	fmt.Println(ver.X, ver.Y)
    	
        //指针调用
    	(&ver).changeVertexByPointer()
    	fmt.Println(ver.X, ver.Y)
    }
    

    通常情况下,建议使用指针,除非为了要避免修改原来实例的值。

    值传递,尤其是复杂的结构体,会做拷贝处理,使用指针性能相对会更好。

    接口实例化

    package main
    
    import (
    	"fmt"
    )
    
    type Vertex struct {
    	X, Y int
    }
    
    type IVertex interface {
    	GetResult() int
    }
    
    func (ver Vertex) GetResult() int {
    	var result int=ver.X + ver.Y
    	fmt.Println("result:",result)
    	return result
    }
    
    func main() {
    	var iv IVertex
    	//iv.GetResult()//这一句会在运行时抛异常:panic: runtime error: invalid memory address or nil pointer dereference
    	var v Vertex
    	iv = v
        fmt.Println(iv,v)//打印{0 0}{0 0}
    	iv.GetResult()//结果是0
    	iv = Vertex{X: 10, Y: 10}
    	iv.GetResult()//结果20
    }
    

    接口类型判断

    var typeVar interface{} = "hello"
    t, result := typeVar.(string)
    fmt.Println(t, result)// hello true
    

    typeVar.(string)如果类型是对的,第二个参数可以不写,得到的是实际的值,如果类型不正确,比如t:=typeVar.(string),将会抛异常interface conversion: interface {} is string, not int,如果t,result:=typeVar.(string),t则是0,result是false

    switch case类型

    var typeVar interface{} = nil
    switch t := typeVar.(type) {
        case int:
        fmt.Println("int:", t)
        default:
        fmt.Printf("%T", t)
    }
    

    切片

    在了解切片之前,先来看看数组,切片是基于数组定义的。

    package main
    
    import "fmt"
    
    func main() {
    	arr:=[6]int{1,2,3,4,5,6}
    	fmt.Println(arr);
    }
    

    数组是个定长的,如果初始化的元素个数少于6个,那么会自动补上零值,多于6个会提示越界

    数组完整的声明写法

    var arr [6]int=[6]int{1,2,3,4,5,6}
    

    那么切片是什么?切片是基于对数组的引用,slice是个结构体,注意slice本身是值传递的

    type slice struct {
    	array unsafe.Pointer
    	len   int
    	cap   int//默认容量于len相等
    }
    

    从这里可以看出,切片修改元素,那么对应的数组的值也跟着修改,多个基于这个数组的切片修改同一个元素,全部都会被修改,下面来看看以下的例子

    package main
    
    import "fmt"
    
    func main() {
    	var	arr [6]int=[6]int{1,2,3,4,5}
    	sliceA:=arr[:]
    	sliceB:=arr[1:3]
    	sliceA[1]=10
    	fmt.Println(sliceB[0])//10
    	fmt.Println(arr[1])//10
    }
    

    切片的一些操作

    package main
    
    import "fmt"
    
    func main() {
    	var arr [6]int = [6]int{1, 2, 3, 4, 5, 6}
    	// 声明切片,arr[:3:5],第一个不写那么值是0,第二个是截取到第(下标-1)的元素,第三个是初始的容量,不写默认是数组的长度,不允许超过数组的长度
    	slice := arr[:3:5]
    	fmt.Println(slice, cap(slice), len(slice))//[1 2 3] 5 3
    	slice = append(slice, 10)//在末尾添加元素,返回一个新的切片
    	slice = append(slice, 20)
    	slice = append(slice, 30)
    	fmt.Println(slice, cap(slice), len(slice))// [1 2 3 10 20 30] 10 6  长度用完,自动扩容
    }
    

    映射

    go的映射实际就是java的map或者是c#的dictionary,定义是map[keyType]valueType

    package main
    
    import (
    	"fmt"
    )
    
    func main() {
    	var maps map[string]int = map[string]int{
    		"tome": 20,
    		"kate": 18,
    		"tub":  45,
    	}
    
    	for key, value := range maps {
    		fmt.Println(key, value)
    	}
    }
    

    map的一些操作

    package main
    
    import (
    	"fmt"
    )
    
    func main() {
        // 初始化
    	var maps Map = Map{
    		"tome": 20,
    		"kate": 18,
    		"tub":  45,
    	}
    	// 存在的key修改
    	maps["tome"]=33
    	// 不存在的key新增
    	maps["heihei"]=43
    	// 判断是否包含key
    	value,exists:=maps["join"]
    	fmt.Println("join is exists:",value,exists)//value是零值
    	value,exists =maps["kate"]
    	fmt.Println("kate is exists:",value,exists)//value对应的是key为kate对应的值
    
    	for key, value := range maps {
    		fmt.Println(key, value)
    	}
    	fmt.Println("=====================")
    	delete(maps,"heihei")
    	for key, value := range maps {
    		fmt.Println(key, value)
    	}
    }
    

    结果

    join is exists: 0 false
    kate is exists: 18 true
    kate 18
    tub 45
    heihei 43
    tome 33
    =====================
    tome 33
    kate 18
    tub 45
    

    泛型

    1.18之前的版本是不支持泛型的,beta1开始新增了对泛型的支持

    type Number interface {//类似c#里做了泛型约束
        int64 | float64
    }
    

    比如数字相加

    package main
    
    import "fmt"
    
    
    func main() {
        intResult:= Add[int](1,1)
        floatResult := Add[float64](1.1,1.1)
        fmt.Println("1 + 1 =",intResult)
        fmt.Println("1.1 + 1.1 =",floatResult)
    }
    
    func Add[T int|float64](num1 T,num2 T) T{
        return num1+num2
    }
    

    得到

    1 + 1 = 2
    1.1 + 1.1 = 2.1
    

    泛型类型实际是个接口类型,比如上面的例子可以这样写

    package main
    
    import "fmt"
    
    type Number interface{
        int|float64
    }
    
    func main() {
        intResult:= Add[int](1,1)
        floatResult := Add[float64](1.1,1.1)
        fmt.Println("1 + 1 =",intResult)
        fmt.Println("1.1 + 1.1 =",floatResult)
    }
    // Number 替代了int|float64
    func Add[T Number](num1 T,num2 T) T{
        return num1+num2
    }
    

    这种定义方式实际是限定了泛型的类型,必须显示指出泛型的类型组合。

    下面来自定义接口

    package main
    
    import "fmt"
    
    type IService interface{
    	GetServiceName() string
    }
    
    type ServiceT interface{
    	IService
    }
    
    type UserService struct{Name string}
    func (service UserService) GetServiceName() string{
    	return service.Name
    }
    
    type RoleService struct{Name string}
    func (service RoleService) GetServiceName() string{
    	return service.Name
    }
    
    
    func  GetService[ST ServiceT](serviceType ST) string{
    	return serviceType.GetServiceName()
    }
    
    func main()  {
    	serviceName:= GetService[UserService](UserService{Name: "userService"})
    	fmt.Println("服务名称:",serviceName)
    	serviceName= GetService[RoleService](RoleService{Name: "roleService"})
    	fmt.Println("服务名称:",serviceName)
    }
    

    得到

    服务名称: userService
    服务名称: roleService
    

    那ServiceT改成空接口,是否能够调用?目前的版本(1.18beta2)是不支持的

    type ServiceT interface{}
    

    ServiceT是空接口会提示.\gen.go:41:14: serviceType.GetServiceName undefined (type ST has no field or method GetServiceName)

    那我们通过反射来看看是否能够找到GetServiceName?

    func  GetService[ST ServiceT](serviceType ST) string{
    	t:= reflect.TypeOf(serviceType)//获取类型
    	v:=reflect.ValueOf(serviceType)//获取值
    	
    	methodCount:= t.NumMethod()
    	for i := 0; i < methodCount; i++ {
    		methodName:=t.Method(i).Name
    		fmt.Println(methodName)
    	 	val:= v.MethodByName(methodName).Call(nil) //调用方法
    		return val[0].String()//把reflect.value转换为string类型
    	}
    	return ""
    }
    

    得到输出

    GetServiceName
    服务名称: userService
    GetServiceName
    服务名称: roleService
    

    go程

    go程是开启了一个线程,如果主进程结束,那么开启的go程也会跟着结束

    package main
    
    import (
    	"fmt"
    	"time"
    )
    
    func say(s string,count int) {
    	for i := 0; i < count; i++ {
    		time.Sleep(100 * time.Millisecond)
    		fmt.Println(s)
    	}
    }
    
    func main() {
    	go say("world",10)
    	   say("hello",3)
     
    }
    

    输出结果

    hello
    world
    world
    hello
    hello
    

    可以看到,当hello输出3次,主进程结束,那么通过go say("world",10)调用的并没有执行完,也跟着结束了

    channel

    发送和接收操作在另一端准备好之前都会阻塞

    信道上的发送操作总在对应的接收操作完成前发生。

    若在信道关闭后从中接收数据,接收者就会收到该信道返回的零值。

    从无缓冲信道进行的接收,要发生在对该信道进行的发送完成之前。

    select

    package main
    
    import (
    	"fmt"
    	"time"
    )
    
    func main() {
        //<-chan time.Time表示该channel只能发送
    	var tick <-chan time.Time = time.Tick(100 * time.Millisecond)
        boom := time.After(500 * time.Millisecond)
    	for {
    		select {
    		case <-tick:
    			fmt.Println("tick")
    		case <-boom:
    			fmt.Println("boom!")
    			return
    		default:
    			fmt.Println("……")
    			time.Sleep(50 * time.Millisecond)
    		}
    	}
    }
    

    单向channel

    package main
    
    import (
    	"fmt"
    	"time"
    )
    //ch只能接收
    func receive(ch chan<- string, str string) {
    	ch <- str
    }
    //sender只能发送,receiver只能接收
    func send(sender <-chan string, receiver chan<- string) {
    	time.Sleep(time.Second)
    	receiver <- <-sender
    }
    
    func main() {
    	var sender chan string = make(chan string)
    	var receiver chan string = make(chan string)
    	go receive(sender, "hi,let's go")
    	go send(sender, receiver)
    	str := <-receiver
    	fmt.Println(str)//输出:hi,let's go
    }
    

    缓冲通道

    package main
    
    import (
    	"fmt"
    )
    
    func main() {
    	var ch chan string = make(chan string)
    	ch <- "hello"
    	str := <-ch
    	fmt.Println(str)
    }
    

    以上的代码会抛出异常:fatal error: all goroutines are asleep - deadlock!

    发送和接收操作在另一端准备好之前都会阻塞,这时候,无缓冲的通道,会造成死锁

    package main
    
    import (
    	"fmt"
    )
    
    func main() {
    	var ch chan string = make(chan string,1)
    	ch <- "hello"
    	str := <-ch
    	fmt.Println(str)
    }
    
  • 相关阅读:
    Django开发个人博客网站
    Photoshop界面字体太小解决方案
    [Leetcode]第三题:无重复字符最长子串
    web网站服务(1)
    备份与恢复笔记和实验
    oracle事物和常用数据库对象笔记和实验
    Oracle配置管理实验
    Oracle配置管理笔记
    Oracle体系结构和用户管理实验
    Oracle数据库部署
  • 原文地址:https://www.cnblogs.com/hunter2014/p/16355157.html
Copyright © 2020-2023  润新知