sailei1 / blog

1 stars 0 forks source link

go 笔记 #94

Closed sailei1 closed 4 years ago

sailei1 commented 4 years ago

golang

变量定义

变量名在前 类型在后 类型相同的可以一次多个 后面跟数据类型 声明之后 有默认值 数据类型 可以省略 让编译器 自己推断 := 这种方式声明 作用域在函数体内 不能用于外部声明 var 这种方式 里面外面都可以声明 作用域在包内部 多个 var (aa =3 s="kkk") 多个声明

string 类型需使用双引号

总结 函数内声明 首页 := 包内声明变量 使用 var()

内建变量类型

bool string

(u)int 其中u开头的类型就是无符号整型。 无符号类型能够表示正整数和零。而有符号类型除了能够表示正整数和零外,还可以表示负整数。 uintptr 是个指针 位数默认操作系统

byte 等同于int8,常用来处理ascii字符 rune 等同于int32,常用来处理unicode或utf-8字符

float32 单精度 float64 双精度 小数点后面能跟的小数位数不同

复数 complex 64 32位 实部跟虚部 都是float 32 complex 128 64位 实部跟虚部 都是float 64

类型转换是强制的,没有隐式转换

  var a,b int =3,4 
  var c int =math.Sqrt(a*a+b*b)  //❎
  var c int =int(math.Sqrt(float64(a*a+b*b)))//✅

解决浮点数运算不准确的问题,在运算前我们把参加运算的数先升级(10的X的次方)到整数,等运算完后再降级(0.1的X的次方)

sailei1 commented 4 years ago

常量 const 名字小写 尽量不要大写,大写代表作用域public const 数值可以作为各种类型使用

枚举
const() iota 自增值种子 const ( b = 1 << (10 * iota) kb mb gb tb pb)

条件语句

if 在 Go 中条件语句不能有括号, 没有三元运算符 if条件里面可以赋值 注意作用域只在if里面

  if num := 9; num < 0 {
        fmt.Println(num, "is negative")
    } else if num < 10 {
        fmt.Println(num, "has 1 digit")
    } else {
        fmt.Println(num, "has multiple digits")
    }

switch 后面可以没有表达式 不用在末位添加break; fallthrough 相当于渗透 必须是最后一个语句

 i := 2
 switch i {
    case 1:
        fmt.Println("one") 
    case 2:
        fmt.Println("two")
        fallthrough
    case 3:
        fmt.Println("three")
    }

  t := time.Now()
    switch {
    case t.Hour() < 12:
        fmt.Println("It's before noon")
    default:
        fmt.Println("It's after noon")
    }

panic 中断程序运行

循环 for 条件里面不能有括号 可以省略 初始条件 结束条件 递增表达式 ;占位

省略 初始条件 增值条件 相当于while 循环

都省略 死循环 break跳出

sailei1 commented 4 years ago

函数 定义 函数名在前 返回类型在后 可以返回多个值 函数体内定义变量 一定要用 否则编译错误 "_" 变量占位 参数没有默认值 函数式编程 函数可以做参数 可以返回函数

 func apply(op func(int, int) int, a, b int) int {
  p := reflect.ValueOf(op).Pointer()
  opName := runtime.FuncForPC(p).Name()
  fmt.Printf("Calling function %s with args "+
    "(%d, %d)\n", opName, a, b)

  return op(a, b)
}

func main() {
  fmt.Println("pow(3, 4) is:", apply(
    func(a int, b int) int {
      return int(math.Pow(
        float64(a), float64(b)))
    }, 3, 4))
}
sailei1 commented 4 years ago

指针 *类型 代表指针 &取地址 不支持指针运算

值传递 值拷贝 外面值不变

func swap(a, b int) {
   a,b=b,a
}
a, b := 3, 4
swap(a, b)
fmt.Println("a, b after swap is:", a, b) //3,4

引用传递 引用拷贝 外面值改变

func swap(a, b *int) {
   *a,*b=*b,*a
}
a, b := 3, 4
  swap(&a, &b)
 fmt.Println("a, b after swap is:", a, b) //4,3

go 只有值传递一种 ,要想改变值 必须要传指针进去

sailei1 commented 4 years ago

数组

数组是值类型 ,[10]int 和[20]int 是不同类型

go 一般不直接使用数组 使用切片


package main

import "fmt"

func editArray(arr *[5]int){
    arr[0]=100
    for i,v :=range arr{
      fmt.Println(i,v)
    }
}

func main() {
   var arr1 [5]int  //有默认值
   arr2:=[3]int{1,3,5}
   arr3:=[...]int{2,4,6}
   grid:=[4][5]int{} // := 需要给个初始值
   fmt.Println(arr1,arr2,arr3)
   fmt.Println(grid)
   for _,v :=range arr3{
    fmt.Println(v)
   }

  fmt.Println("before:",arr1)
  editArray(&arr1) //修改数组 需传指针进去
  fmt.Println("after:",arr1) 
}
sailei1 commented 4 years ago

Slice

半开半闭区间 包含第一位,不包含最后一位 Slice 本身没数据 对底层array的 一个视图

Reslice 引用的是同一个数组

import "fmt"

func main() {
   var s []int  //切片默认值nil
  arr:=[]int{0,1,2,3,4,5,6,7}
  fmt.Println("arr:",arr)
  s1:=arr[2:6]
  s2:=s1[3:5]
  fmt.Println("s1:",s1) //[2,3,4,5]
  fmt.Println("s2:",s2) //[5,6]
  fmt.Println("arr[:]",arr[:]) //arr[:] [0 1 2 3 4 5 6 7]
  // slice 可以向后扩展,不可以向前扩展
  //s【i】 不可以超越len(s),向后扩展不可以超越底层数组cap(s)
  fmt.Printf("s1=%v,len(s1)=%d,cap(s1)=%d\n",s1,len(s1),cap(s1))//s1=[2 3 4 5],len(s1)=4,cap(s1)=4

  fmt.Printf("s2=%v,len(s2)=%d,cap(s2)=%d\n",s1,len(s2),cap(s2))//s2=[2 3 4 5],len(s2)=2,cap(s2)=2
}

向Slice 添加元素 append() 可以一次添加多个

添加元素时如果超越cap,系统会重新分配更大的底层数组, 原数组拷贝到大数组里,原数组没引用 进行垃圾回收

由于值传递的关系,必须接收append的返回值 s=append(s,v) cap(s) 2倍的扩充

// 切片常规操作

func printSlice(s []int){
   fmt.Printf("s=%v,len(s)=%d,cap(s)=%d\n",s,len(s),cap(s))
}
func main() {
    s1:=[]int{2,4,6,8}
    printSlice(s1) //s=[2 4 6 8],len(s)=4,cap(s)=4
    s2:=make([]int ,10,32) //长度为10 容量32
    s2[5]=-1
    s2=append(s2,100)
    printSlice(s2)  //s=[0 0 0 0 0 -1 0 0 0 0 100],len(s)=11,cap(s)=32

    fmt.Println("copy")
    copy(s2,s1) //把s1 复制给s2
    printSlice(s2) //s=[2 4 6 8 0 -1 0 0 0 0 100],len(s)=11,cap(s)=32

    fmt.Println("deleteByIndex 4")
    s2=append(s2[:3],s2[4:]...)
    printSlice(s2)  //s=[2 4 6 0 -1 0 0 0 0 100],len(s)=10,cap(s)=32

    fmt.Println("shift()")
    t:=s2[0]
    s2=s2[1:]
    fmt.Println("first:",t) //2
    printSlice(s2)  //s=[4 6 0 -1 0 0 0 0 100],len(s)=9,cap(s)=31

    fmt.Println("pop()")
    f:=s2[len(s2)-1]
    s2=s2[:len(s2)-1]
    fmt.Println("last:",f) //100
    printSlice(s2) //s=[4 6 0 -1 0 0 0 0],len(s)=8,cap(s)=31
}
sailei1 commented 4 years ago

Map

m:=map[k]{v} // k 无固定顺序 hashmap key 不存在 取 类型默认值 m2:=make(map[string]int) //m2 ==empty map var m3 map[string]int //m3==nil delete(m,key) //删除

func main() {
     m:=map[string]string{
      "name":"我",
      "course":"golang",
     }
     for k,v :=range m{  
        fmt.Println(k,v)
     }
     //  map key 注意点
     // key 不保证顺序    把key 加到slice里面 循环slice 保证顺序
     //map使用哈希表  必须保证相等
     //类型 slice  map, function 不能判断相等  不能作为key
     //自定义结构 包含上述字段 也不能作为key

  fmt.Println("Deleting values")
  name, ok := m["name"]
  fmt.Printf("m[%q] before delete: %q, %v\n",
    "name", name, ok)

  delete(m, "name")
  name, ok = m["name"]
  fmt.Printf("m[%q] after delete: %q, %v\n",
    "name", name, ok)
sailei1 commented 4 years ago

String 每个中文 占3个字节(ascii码)
[]rune(s) 每个rune4个字节

 func main() {
   s:="学习golang!"

  fmt.Printf("%v Rune count:%v \n",
    s,utf8.RuneCountInString(s)) //学习golang! Rune count:9 

  bytes := []byte(s)
  for len(bytes) > 0 {
    ch, size := utf8.DecodeRune(bytes)
    bytes = bytes[size:]
    fmt.Printf("%c ", ch)  //学 习 g o l a n g ! 
  }
  fmt.Println()

  for i, ch := range []rune(s) { //中文转换
    fmt.Printf("(%d %c) ", i, ch) //(0 学) (1 习) (2 g) (3 o) (4 l) (5 a) (6 n) (7 g) (8 !)
  }
}

常用方法 fields 分割空格 split join //分割 连接 contains,Index 查找 Tolower Toupper 大小写 Trim, TrimRight,Trimleft, 去掉空格

sailei1 commented 4 years ago

Printf 占位转换

%d          十进制整数
%x, %o, %b  十六进制,八进制,二进制整数。
%f, %g, %e  浮点数: 3.141593 3.141592653589793 3.141593e+00
%t          布尔:true或false
%c          字符(rune) (Unicode码点)
%s          字符串
%q          带双引号的字符串"abc"或带单引号的字符'c'
%v          变量的自然形式(natural format)
%T          变量的类型
%%          字面上的百分号标志(无操作数)
sailei1 commented 4 years ago

struct

//go  只支持封装  不支持继承和多态
//go 没有class 只有 struct
//go 没有构造函数

type treeNode struct {
    value int
    left,right *treeNode
}
func (node treeNode) print(){
    fmt.Printf("%v\n", node.value)
}
//定义方法  (node *treeNode)接收者  只有指针才可以改变结构内容
//nil 指针也可以调用方法
func (node *treeNode) setValue(v int)  {
    if node !=nil {
        node.value = v
    }else{
        fmt.Println("node is nil")
    }
}
// 指针接收者
// 要改变内容必须使用指针接受者
// 结构过大也可以考虑使用指针接收者 (值接收者是拷贝  结构过大会有性能问题)
// 一致性  如有指针接收者,最好都是指针接收者
func (node *treeNode) traverse(){
      if node ==nil{
        return
      }
      node.left.traverse()
      node.print()
      node.right.traverse()

}

//go 局部变量地址 可以直接给外面用
func createNode(v int) *treeNode{
    return &treeNode{value:v}
}

func main() {
      //var root  Node
      root :=treeNode{value:3}
      root.left=&treeNode{}
      root.right=&treeNode{5,nil,nil}
      root.right.left=new(treeNode)
      root.left.right=createNode(2) //go 指针 可以直接用.访问成员
      //fmt.Println(root)
      root.setValue(100)
       root.traverse()
      //root.print()

    //  nodes:=[]treeNode{
    //         {value:3},
    //         {},
    //         {6,nil,&root},
    //  }
    //fmt.Println(nodes)

     //pRoot :=&root
     //pRoot.setValue(1000)
     //pRoot.print()
     //root.print()

    // var pRoot *treeNode
    //pRoot.setValue(200)
    //pRoot =&root
    //pRoot.setValue(1200)
    //pRoot.print()

}
sailei1 commented 4 years ago

包和封装

名字一般驼峰命名规则
针对包来说 首字母大写 public 首字母小写 private 适用于 常量 方法 结构 等定义 每个目录只能有一个包 为结构定义的方法必须放在同一个包内,可以不同文件 本地导包 项目需放在 $GOPATH/src 目录下

扩展

 package main

import "fmt"
//使用别名

type Queue []int
func (q *Queue)Push(v int){
  *q=append(*q,v)  
}
func (q *Queue)Pop() int{
  head :=(*q)[0]
  *q=(*q)[1:]
  return head
}
func (q *Queue) IsEmpty() bool{
  return len(*q)==0
}

func main() {
  q:=Queue{1}
  q.Push(2)
  q.Push(3)
  fmt.Println(q.Pop())
  fmt.Println(q.Pop())
  fmt.Println(q.IsEmpty())
  fmt.Println(q.Pop())
  fmt.Println(q.IsEmpty())
}

gopath 目录结构

每个目录下 只能有一个package main

go build go install ./... 安装 go run 直接编译运行

sailei1 commented 4 years ago

接口
go 用接口 完成其他语言的继承跟多态 接口由使用者定义 跟其他语言不太一样 一个对象只要全部实现了接口中的方法,那么就实现了这个接口。Go语言中不同的类型可以实现同一个接口

// 使用者
package main

import (
  "fmt"
  "projects/golearn/retriever/mock"
)

type Retriever interface { //接口由使用者定义
  Get(url string) string
}

func download(r Retriever) string {
  return r.Get("www.baidu.com")
}

func main() {
  r := mock.R{Contents: "xxxx"}
  fmt.Println(download(r))
}

// 实现者
package mock

type R struct {
  Contents string
}

func (r R) Get(url string) string { //接口实现 只要方法名跟接口定义一样就行
  return r.Contents
}

接口的值类型 接口变量 包含 实现者的类型 跟 实现者的值 (实现者的指针指向实现者) 接口变量 自带指针 接口变量 同样采用值传递,几乎不需要使用接口的指针 指针接收者实现只能以指针方式使用;值接收者两者都可

接口类型判断 x.(T) x:表示类型为interface{}的变量 T:表示断言x可能是的类型 interface{} 表示任何类型

sailei1 commented 4 years ago

接口组合 类似于继承

// 使用者
package main

import (
  "fmt"
  "projects/golearn/retriever/mock"
)

const url = "https://m.baidu.com"

type Retriever interface { //接口由使用者定义
  Get(url string) string
}

type Poster interface {
  Post(url string, form map[string]string)
}

type RetrieverPoster interface {
  Retriever
  Poster
  TestMethod(s string)
}

func session(s RetrieverPoster) string { 
  s.Post(url, map[string]string{
    "contents": "xxxx ffff",
  })
  return s.Get(url)
}

func main() {
  r := &mock.R{}
  fmt.Println(session(r))
  r.TestMethod("123abc")

}

// 实现者
package mock

import "fmt"

type R struct {
  Contents string
}

func (r *R) Get(url string) string { //接口实现 只要方法名跟接口定义一样就行
  fmt.Println("Get")
  return r.Contents
}
func (r *R) Post(url string, form map[string]string) {
  fmt.Println("Post")
  r.Contents = form["contents"]
  return
}
func (r *R) TestMethod(s string) {
  fmt.Println("TestMethod: ", s)
}
sailei1 commented 4 years ago

函数式编程

参数 变量 返回值 都可以是函数 闭包 跟JS 差不多

package main

import "fmt"

func adder(sum int) func(int) int {
  return func(v int) int {  //闭包
    sum += v
    return sum
  }
}

type iAdder func(int) (int, iAdder) 

func adder2(base int) iAdder {

  return func(i int) (int, iAdder) {
    return base + i, adder2(base + i)
  }
}

func main() {
  s, t := adder(0), 0
  for i := 0; i < 10; i++ {
    t = s(i)
  }

  //a, t := adder2(0), 0
  //for i := 0; i < 10; i++ {
  //  t, a = a(i)
  //
  //}
  fmt.Println(t)
}

//为函数实现接口

package main

import (
  "bufio"
  "fmt"
  "io"
  "strings"
)

//为函数实现接口
type intGen func() int

func Fib() intGen {
  a, b := 0, 1
  return func() int {
    a, b = b, a+b
    return a
  }
}

func (g intGen) Read(p []byte) (n int, err error) {
  next := g()
  if next > 1000 {
    return 0, io.EOF
  }
  s := fmt.Sprintf("%d\n", next) //int 转string
  return strings.NewReader(s).Read(p)
}

func printFileContents(reader io.Reader) {
  scanner := bufio.NewScanner(reader)

  for scanner.Scan() {
    fmt.Println(scanner.Text())
  }
}

func main() {
  f := Fib() //
  printFileContents(f)
}
sailei1 commented 4 years ago

defer

如果是列表有个运行栈 先进后出 遇到return panic 还是会执行 确保调用在函数结束时发生 参数在defer 语句时计算

一般常用于 打开 怕你忘记关闭的逻辑 相当于 JS setTimeout(()=>{},0)

package main

import (
    "bufio"
    "fmt"
    "os"
    "projects/golearn/functional/fib"
)

func tryDefer() {
    for i := 0; i < 100; i++ {
        defer fmt.Println(i) // 会记住当前变量的值
        if i == 30 {
            panic("print too many")
        }
    }

}

func writeFile(fileName string) {

    file, err := os.Create(fileName)
    if err != nil {
        panic(err)
    }
    defer file.Close()   //最后执行
    writer := bufio.NewWriter(file)
    defer writer.Flush()  //先执行 
    f := fib.Fibonacci()

    for i := 0; i < 20; i++ {
        fmt.Fprintln(writer, f())
    }

}

func main() {
    //tryDefer()
    writeFile("fib.txt")
}
sailei1 commented 4 years ago

panic (尽量少用) 停止当前函数执行 一直向上返回,执行每一层的defer 如果没有遇见recover 程序退出

recover 仅在defer调用中使用 获取panic的值 如果无法处理,可重新panic

package main

import "fmt"

func tryRecover() {
    defer func() {
        r := recover()
        if err, ok := r.(error); ok {
            fmt.Println("Error occurred", err)
        } else {
            panic(fmt.Sprintf("sai's panic: %s", r))
        }

    }()
    b := 0
    a := 5 / b
    fmt.Println(a)

    //panic(123)
}

func main() {
    tryRecover()
}
sailei1 commented 4 years ago
//handler.go
package filelist

import (
    "fmt"
    "io/ioutil"
    "net/http"
    "os"
    "strings"
)

type userError string

//实现 userError 接口
func (e userError) Error() string {
       //return e.Error() // 调用原生报错
    return e.Message()
}
func (e userError) Message() string { //实现 外部userError接口  Message()
    return string(e)
}

const prefix = "/list/"

func Handler(writer http.ResponseWriter, request *http.Request) error {

    if strings.Index(request.URL.Path, prefix) != 0 {
        return userError(fmt.Sprintf("path %s must start "+
            "with %s",
            request.URL.Path, prefix))
    }

    path := request.URL.Path[len(prefix):]
    file, err := os.Open(path)
    if err != nil {
        //panic(err)
        //http.Error(writer, err.Error(), http.StatusInternalServerError)
        return err
    }

    defer file.Close()
    all, err := ioutil.ReadAll(file)
    if err != nil {
        return err
    }
    writer.Write(all)
    return nil
}
\\web.go
package main

import (
    "log"
    "net/http"
    "os"
    "projects/golearn/errhanding/filelistserver/filelist"
)

//定义处理函数  appHandler 返回error
type appHandler func(writer http.ResponseWriter, request *http.Request) error

type userError interface {
    error
    Message() string //自定义输出报错消息方法
}

//appHandler 当参数传过去   捕捉 处理error
func errWrapper(handler appHandler) func(writer http.ResponseWriter, request *http.Request) {

    return func(writer http.ResponseWriter, request *http.Request) {
        err := handler(writer, request)

        defer func() {
            if r := recover(); r != nil {
                log.Printf("Panic: %v", r)
                http.Error(writer,
                    http.StatusText(http.StatusInternalServerError),
                    http.StatusInternalServerError)
            }
        }()

        if err != nil {
            code := http.StatusOK
            log.Printf("Error handling  request: %s", err)

            if userErr, ok := err.(userError); ok {
                http.Error(writer, userErr.Error(), http.StatusBadRequest)
                return
            }

            switch {
            case os.IsNotExist(err):
                code = http.StatusNotFound
                //fallthrough
            case os.IsPermission(err):
                code = http.StatusForbidden
            default:
                code = http.StatusInternalServerError
            }

            http.Error(writer, http.StatusText(code), code)
        }
    }
}

func main() {
    http.HandleFunc("/", errWrapper(filelist.Handler)) //调用
    err := http.ListenAndServe(":8888", nil)
    if err != nil {
        panic(err)
    }
}
sailei1 commented 4 years ago

goroutine
每一个并发的执行单元叫作一个goroutine go run -race *.go 检测数据访问冲突 调度器运行协程 在合适的点进行切换 程序不能完全控制 goroutine 可能的切换点( I/O, select 切换 、channel 、等待锁、runtime.Gosched() 、函数调用)

channel goroutine 双向交互的通道 channel 单向操作会造成死锁 并发基于csp模型 (https://www.jianshu.com/p/36e246c6153d

package main

import (
  "fmt"
  "time"
)

func worker(id int, c chan int) {
  for n := range c {
    fmt.Printf("worker %d received %c\n", id, n)
  }
}

func createWorker(id int) chan<- int { //通道取值
  c := make(chan int)
  go worker(id, c)
  return c
}

func chanDemo() {
  var channels [10]chan<- int
  for i := 0; i < 10; i++ {
    channels[i] = createWorker(i)
  }

  for i := 0; i < 10; i++ {
    channels[i] <- 'a' + i //通道存值 阻塞式 必须要有人收
  }

  for i := 0; i < 10; i++ {
    channels[i] <- 'A' + i //
  }

  time.Sleep(time.Millisecond) //等待 确保函数并发执行完  否则main函数运行完会关闭
}

func bufferedChannel() {
  c := make(chan int, 3)
  go worker(20, c)
  c <- 'a'
  c <- 'b'
  c <- 'c'
  c <- 'd'
  time.Sleep(time.Millisecond)
}

func channelClose() {
  c := make(chan int)
  go worker(30, c)
  c <- 'a'
  c <- 'b'
  c <- 'c'
  c <- 'd'
  close(c) //关闭通道 发送方发起
  time.Sleep(time.Millisecond)
}

func main() {
  chanDemo()
  bufferedChannel()
  channelClose()

}
package main

import (
  "fmt"
)

func doWork(id int, c chan int, done chan bool) { //不要通过共享内存来通信;通过通信来共享内存
  for n := range c {
    fmt.Printf("worker %d received %c\n", id, n)
    go func() {
      done <- true
    }()
  }
}

type worker struct {
  in   chan int //struct 没有,号分割
  done chan bool
}

func createWorker(id int) worker { //通道取值
  worker := worker{
    in:   make(chan int),
    done: make(chan bool),
  }
  go doWork(id, worker.in, worker.done)
  return worker
}

func chanDemo() {
  var workers [10]worker
  for i := 0; i < 10; i++ {
    workers[i] = createWorker(i)
  }

  for i, worker := range workers {
    worker.in <- 'a' + i //通道存值  (阻塞式 必须要有取的操作)
  }

  for i, worker := range workers {
    worker.in <- 'A' + i //
  }
  for _, worker := range workers {
    <-worker.done
    <-worker.done
  }

}

func main() {
  chanDemo()
}
package main

import (
  "fmt"
  "sync"
)

func doWork(id int, w worker) {
  for n := range w.in {
    fmt.Printf("worker %d received %c\n", id, n)
    //go func() {
    //  done <- true
    //}()
    w.done()
  }
}

type worker struct {
  in chan int //struct 没有,号分割
  //done chan bool
  done func()
}

func createWorker(id int, wg *sync.WaitGroup) worker { //通道取值
  worker := worker{
    in: make(chan int),
    done: func() {
      wg.Done()
    },
  }
  go doWork(id, worker)
  return worker
}

func chanDemo() {
  var workers [10]worker
  var wg sync.WaitGroup //
  for i := 0; i < 10; i++ {
    workers[i] = createWorker(i, &wg)
  }

  wg.Add(20)
  for i, worker := range workers {
    worker.in <- 'a' + i //通道存值  (阻塞式 必须要有取的操作)
  }

  for i, worker := range workers {
    worker.in <- 'A' + i //
  }
  wg.Wait()

}

func main() {
  chanDemo()
}
sailei1 commented 4 years ago

select channel 不确定谁先到的时候 用select select 使用defalut 会变成非阻塞式获取 select 中使用Nil channel 会被阻塞到 永远不会执行

package main

import (
    "fmt"
    "math/rand"
    "time"
)

func generator() chan int {
    out := make(chan int)
    go func() {
        i := 1
        for {
            time.Sleep(
                time.Duration(rand.Intn(1500)) *
                    time.Millisecond)
            out <- i
            i++
        }
    }()
    return out
}

func worker(id int, c chan int) {
    for n := range c {
        time.Sleep(time.Second)
        fmt.Printf("Worker %d received %d\n",
            id, n)
    }
}

func createWorker(id int) chan<- int {
    c := make(chan int)
    go worker(id, c)
    return c
}

func main() {
    var c1, c2 = generator(), generator()
    var worker = createWorker(-1)
    //var w chan int // nil channel   select{ case w<-n: 会被一直阻塞 永远不会执行 }

    var values []int
    tm := time.After(10 * time.Second) //定时器
    tick := time.Tick(time.Second)     //每秒执行一次
    for {
        var activeWorker chan<- int
        var activeValue int
        if len(values) > 0 {
            activeWorker = worker
            activeValue = values[0]
        }

        select {
        case n := <-c1:
            values = append(values, n)
        case n := <-c2:
            values = append(values, n)
        case activeWorker <- activeValue:
            values = values[1:]

        case <-time.After(500 * time.Millisecond): //两次执行相邻的时间差
            fmt.Println("timeout")
        case <-tick:
            fmt.Println(
                "queue len =", len(values))
        case <-tm:
            fmt.Println("bye")
            return
        }
    }
}
sailei1 commented 4 years ago

传统同步机制

package main

import (
    "fmt"
    "sync"
    "time"
)

type atomicInt struct {
    value int
    lock  sync.Mutex
}

func (a *atomicInt) increment() {
    fmt.Println("safe increment")
    func() {
        a.lock.Lock()
        defer a.lock.Unlock()

        a.value++
    }()
}

func (a *atomicInt) get() int {
    a.lock.Lock()
    defer a.lock.Unlock()

    return a.value
}

func main() {
    var a atomicInt
    a.increment()
    go func() {
        a.increment()
    }()
    time.Sleep(time.Millisecond)
    fmt.Println(a.get())
}
sailei1 commented 4 years ago

迷宫的广度优先搜索 maze.ini

6 5
0 1 0 0 0
0 0 0 1 0
0 1 0 1 0
1 1 1 0 0
0 1 0 0 1
0 1 1 0 0
package main

import (
    "fmt"
    "os"
)

func readMaze(fileName string) [][]int {
    file, err := os.Open(fileName)
    if err != nil {
        panic(err)
    }
    var row, col int
    fmt.Fscanf(file, "%d %d", &row, &col)
    maze := make([][]int, row)
    for i := range maze {
        maze[i] = make([]int, col)
        for j := range maze[i] {
            fmt.Fscanf(file, "%d", &maze[i][j])
        }
    }
    return maze

}

type point struct {
    i, j int
}

var dirs = [4]point{
    {-1, 0}, {0, -1}, {1, 0}, {0, 1}} //上左下右
func (p point) add(r point) point {
    return point{p.i + r.i, p.j + r.j}
}
func (p point) at(grid [][]int) (int, bool) {
    if p.i < 0 || p.i >= len(grid) {
        return 0, false
    }
    if p.j < 0 || p.j >= len(grid[p.i]) {
        return 0, false
    }

    return grid[p.i][p.j], true
}

func walk(maze [][]int, start, end point) [][]int {
    steps := make([][]int, len(maze))
    for i := range steps {
        steps[i] = make([]int, len(maze[i]))
    }
    Q := []point{start}
    for len(Q) > 0 {
        cur := Q[0]
        Q = Q[1:]

        if cur == end {
            break
        }

        for _, dir := range dirs {
            next := cur.add(dir)
            val, ok := next.at(maze)
            if !ok || val == 1 {
                continue //障碍物 跟越界
            }
            val, ok = next.at(steps)
            if !ok || val != 0 {
                continue //已经走过
            }
            if next == start {
                continue //返回到起点
            }
            curStep, _ := cur.at(steps)
            steps[next.i][next.j] = curStep + 1 //标记路径
            Q = append(Q, next)                 //把路径添加到队列里去
        }

    }
    return steps
}

func getPath(steps [][]int, end point) ([]point, bool) {

    var paths []point
    end_value := steps[end.i][end.j]

    if end_value == 0 {
        //没有到达重点
        return paths, false
    }

    paths = append(paths, end) //将终点先存进去
    for {
        if end_value <= 0 {
            break
        }
        end_value--
        for _, p := range dirs {
            next := end.add(p) //寻找周围的点
            data, ok := next.at(steps)

            if !ok || data != end_value {
                continue
            }
            paths = append(paths, next) //找到下一个点
            end = next
        }

    }
    return paths, true
}

func main() {
    maze := readMaze("maze/maze.ini")
    //for _, row := range maze {
    //  for _, val := range row {
    //      fmt.Printf("%3d", val)
    //  }
    //  fmt.Println()
    //}

    steps := walk(maze, point{0, 0},
        point{len(maze) - 1, len(maze[0]) - 1})

    for _, row := range steps {
        for _, val := range row {
            fmt.Printf("%3d", val)
        }
        fmt.Println()
    }

    path, ok := getPath(steps, point{len(maze) - 1, len(maze[0]) - 1})
    if ok {
        fmt.Println("最短路径坐标:")
        for i := len(path) - 1; i >= 0; i-- {
            fmt.Printf("%v ", path[i])
        }
    } else {
        fmt.Println("对不起,没有找到通往终点的路径")
    }

}