切换导航
{{systemName}}
{{ info.Title }}
{{info.Title}}
{{ menu.Title }}
{{menu.Title}}
登录
|
退出
搜索
秘籍列表
Golang从入门到精通
为什么选择Golang (试看)
环境安装与多种IDE配置
基本语法 (试看)
golang并发爆破
开发Golang接口
开发WebSocket和Socket
使用GRPC
操作数据库
Golang微服务项目
面试题
基本语法
[TOC] ####1.创建hello world ``` package main import ("fmt" "bufio" "os") func main(){ fmt.Println("hello world!") fmt.Println("输入任意字符退出!") str2 := getInputByScanner() fmt.Println(str2) } func getInputByScanner() string { var str string //使用os.Stdin开启输入流 in := bufio.NewScanner(os.Stdin) if in.Scan() { str = in.Text() } else { str = "Find input error" } return str } ``` cmd到该目录执行 ``` go build hell.go ``` 会编译出现hell.exe这个可以直接运行。还可以跨平台编译。 ``` go run ``` 像脚本一样使用 ####2.基础 程序的作用: >所有的程序无非就是业务逻辑+数据,数据存在内存中,我们到内存找数据,根据业务处理数据。本来要010101找数据,我们为了可读性可以用名称找到数据。 #####1.变量 特点是名称在前,类型在后 ``` var (name string age int) var newName string ``` ``` package main import "fmt" func main(){ var (name string age int) name="go" age=8 fmt.Println(name) fmt.Println(age) } ``` ``` package main import "fmt" var ( //包变量不可:简写,但是可以简写 aaa = 3 bbb = "abc" ) func varsnuberInit() { var num1, num2 int = 1, 2 //变量定义后必须用到 fmt.Println(num1, num2) } func vartypedecumetion() { var a, b, c, d = 3, 4, true, "ssss" fmt.Println(a, b, c, d) } func varshort() { a, b, c, d := 3, 4, true, "ssss" fmt.Println(a, b, c, d) } func main() { fmt.Println("hello world!") varsnuberInit() //初始化多个变量并必须使用到 vartypedecumetion() //自主判断类型 varshort() //简写 } ``` #####2.常量和枚举 不需要改变的值 ``` const pi=3.1415 const e=2.7182 ``` ``` package main import "fmt" func enums() { const ( cpp = iota java golang csharp c ) const ( b = 1 << (10 * iota) kb mb gb tb pb ) fmt.Println(b, kb, gb, tb, pb) } func main() { enums() } ``` #####3.基本数据类型 ``` func changeString() { s1 := "big" // 强制类型转换 byteS1 := []byte(s1) byteS1[0] = 'p' fmt.Println(string(byteS1)) s2 := "白萝卜" runeS2 := []rune(s2) runeS2[0] = '红' fmt.Println(string(runeS2)) } ``` 首先我先为大家介绍一下GO是怎么支持多语言的,rune相当于Go语言中的char。 关键字`range`可用于循环,类似迭代器操作,它可以遍历`slice`,`array`,`string`,`map`和`channel`,然后返回索引或值。可以使用”_”来忽略不想要的返回值。 ``` package main import ( "fmt" "unicode/utf8" ) func main() { s := "Hi,这里是机遇屋!" fmt.Println(len(s)) //中文算3字节,加起来正好22 for _, value := range []byte(s) { fmt.Printf("%X ", value) //打印占用字节数 } fmt.Println() for i, value := range s { fmt.Printf("(%d %X)", i, value) //打印位置和rune } println("rune count:", utf8.RuneCountInString(s)) //打印字符数 fmt.Println() bytes := []byte(s) for len(bytes) > 0 { ch, size := utf8.DecodeRune(bytes) bytes = bytes[size:] //已取出的过滤掉 fmt.Printf("%c ", ch) } fmt.Println() for i, ch := range []rune(s) { fmt.Printf("(%d %c)", i, ch) } fmt.Println() } ``` #####4.流程控制 ``` package main import "fmt" func whiles() { num := 0 for num < 100 { fmt.Printf("%d", num) num++ } for { fmt.Printf("相当于while无限循环") } } func main() { whiles() } ``` ``` func breakDemo1() { BREAKDEMO1: for i := 0; i < 10; i++ { for j := 0; j < 10; j++ { if j == 2 { break BREAKDEMO1 } fmt.Printf("%v-%v\n", i, j) } } fmt.Println("...") } ``` ``` package main import ( "fmt" "io/ioutil" ) func readTXT() { const filename = "1.txt" contents, err := ioutil.ReadFile(filename) if err != nil { fmt.Println(err) } else { fmt.Printf("%s", contents) } } func main() { readTXT() } ``` ``` package main import ( "fmt" "strconv" ) func switchtest(score int) string { ret := "" switch { case score < 0 || score > 100: ret = "error score=" + strconv.Itoa(score) case score < 60: ret = "F" case score < 70: ret = "D" case score < 80: ret = "C" case score < 90: ret = "B" case score <= 100: ret = "A" } return ret } func main() { fmt.Println( switchtest(1), switchtest(60), switchtest(70), switchtest(80), switchtest(90), switchtest(100), switchtest(-100), switchtest(101), ) } ``` #####5.运算符 算术运算符 关系运算符 逻辑运算符 位运算符 赋值运算符 #####6.数组 数组是同一种数据类型元素的集合。 在Go语言中,数组从声明时就确定,使用时可以修改数组成员,但是数组大小不可变化。 基本语法: ``` // 定义一个长度为3元素类型为int的数组a var a [3]int ``` ``` package main import "fmt" func main() { var testArray = [3]int{} var numArray = [3]int{1, 2} var cityArray = [3]string{"北京", "上海", "深圳"} fmt.Println(testArray) fmt.Println(numArray) fmt.Println(cityArray) } ``` #####7.切片 切片(Slice)是一个拥有相同类型元素的可变长度的序列。它是基于数组类型做的一层封装。它非常灵活,支持自动扩容。 切片是一个引用类型,它的内部结构包含地址、长度和容量。切片一般用于快速地操作一块数据集合。 ``` func main() { // 声明切片类型 var a []string //声明一个字符串切片 var b = []int{} //声明一个整型切片并初始化 var c = []bool{false, true} //声明一个布尔切片并初始化 var d = []bool{false, true} //声明一个布尔切片并初始化 fmt.Println(a) //[] fmt.Println(b) //[] fmt.Println(c) //[false true] fmt.Println(a == nil) //true fmt.Println(b == nil) //false fmt.Println(c == nil) //false // fmt.Println(c == d) //切片是引用类型,不支持直接比较,只能和nil比较 } ``` ``` package main import "fmt" func main() { // 基于数组定义切片 a := [5]int{55, 56, 57, 58, 59} b := a[1:4] //基于数组a创建切片,包括元素a[1],a[2],a[3] var c=[]int{55, 56, 57, 58, 59}//切片 fmt.Println(b) //[56 57 58] fmt.Printf("type of b:%T\n", b) //type of b:[]int fmt.Println(c) } ``` #####8.map map是一种无序的基于key-value的数据结构,Go语言中的map是引用类型,必须初始化才能使用。 ``` func main() { scoreMap := make(map[string]int, 8) scoreMap["张三"] = 90 scoreMap["小明"] = 100 fmt.Println(scoreMap) fmt.Println(scoreMap["小明"]) fmt.Printf("type of a:%T\n", scoreMap) } ``` #####9.函数 1.定义 2.参数 3.返回值 无返回值 单反回值 多返回值 ``` func calc(x, y int) (int, int) { sum := x + y sub := x - y return sum, sub } ``` 命名返回值 ``` func calc(x, y int) (sum, sub int) { sum = x + y sub = x - y return } ``` 但一般都是返回一个值,另一个字段是返回err ``` package main import "fmt" func eval(a, b int, o string) (int, error) { switch o { case "+": return a + b, nil case "-": return a - b, nil case "*": return a * b, nil case "/": return a / b, nil default: return 0, fmt.Errorf("unsupported operation %s", o) } } func main() { if result, err := eval(1, 2, "+"); err != nil { fmt.Println("error :", err) } else { fmt.Println(result) } if result, err := eval(5, 2, "%"); err != nil { fmt.Println("error :", err) } else { fmt.Println(result) } } ``` 可变函数 ``` package main import "fmt" func sumArgs(values ...int) int { ret := 0 for i := range values { ret += values[i] } return ret } func main() { mysum := sumArgs(1, 2, 3, 4, 5, 6) fmt.Println(mysum) } ``` 函数的高深用法 先看一看func 的基本构成元素 ``` func (p myType ) funcName ( a, b int , c string ) ( r , s int ) { return } ``` 其中: - 关键字———func // 这个是定义函数的关键字 - 函数拥有者—(p myType) // 这个是此函数的拥有者,下面解释(此项可省略) - 方法名———funcName // 这个是定义函数的名字 - 入参———— a,b int,b string // 这个是定义函数的入参 - 返回值——— r,s int // 这个是定义函数的返回值,golang可以返回多个值 - 函数体——— { } 重点说说这个函数拥有者(p myType),这个是相较于C/C++比较特殊的地方。 为特定类型定义函数,即为类型对象定义方法 在Go中通过给函数标明所属类型,来给该类型定义方法,上面的 (p myType) 即表示给myType声明了一个方法, p myType 不是必须的。如果没有,则纯粹是一个函数。 下面就是为Mssql类型定义了一个Open函数。 ``` func (m *Mssql) Open() (err error) { var conf []string conf = append(conf, "Provider=SQLOLEDB") conf = append(conf, "Data Source="+m.dataSource) if m.windows { // Integrated Security=SSPI 这个表示以当前WINDOWS系统用户身去登录SQL SERVER服务器(需要在安装sqlserver时候设置), // 如果SQL SERVER服务器不支持这种方式登录时,就会出错。 conf = append(conf, "integrated security=SSPI") } conf = append(conf, "Initial Catalog="+m.database) conf = append(conf, "user id="+m.sa.user) conf = append(conf, "password="+m.sa.passwd) m.DB, err = sql.Open("adodb", strings.Join(conf, ";")) if err != nil { return err } return nil } ``` 用属下方式来调用,先实例化一个Mssql类型,再调用Open ``` db := Mssql{ dataSource: "hddf021.my3w.com", database: "hds1sf21_db", // windwos: true 为windows身份验证,false 必须设置sa账号和密码 windows: false, sa: SA{ user: "hdssdf021", passwd: "", }, } // 连接数据库 err := db.Open() if err != nil { fmt.Println("sql open:", err) return 0 } defer db.Close() ``` 如上代码,实例化一个 Mssql 类型变量db ,就可以调用db.Open来执行这个Mssql类型专属函数。 #####10.指针 任何程序数据载入内存后,在内存都有他们的地址,这就是指针。而为了保存一个数据在内存中的地址,我们就需要指针变量。 比如,“永远不要低估自己”这句话是我的座右铭,我想把它写入程序中,程序一启动这句话是要加载到内存(假设内存地址0x123456),我在程序中把这段话赋值给变量A,把内存地址赋值给变量B。这时候变量B就是一个指针变量。通过变量A和变量B都能找到我的座右铭。 Go语言中的指针不能进行偏移和运算,因此Go语言中的指针操作非常简单,我们只需要记住两个符号:&(取地址)和*(根据地址取值)。 ``` func main() { var a int a = 10 b := &a fmt.Printf("a:%d ptr:%p\n", a, &a) // a:10 ptr:0xc00001a078 fmt.Printf("b:%p type:%T\n", b, b) // b:0xc00001a078 type:*int fmt.Println(&b) // 0xc00000e018 } ``` 其中int类型地址不能赋值给string类型的。go语言中不能操作指针,只能读取,所以安全。 #####11.new和make的用法 new用来初始化指针。 make用来初始化slice、map、chan ``` package main import "fmt" func main() { // 得到int类型的指针 var a=new(int) fmt.Println(a) *a=10 fmt.Println(a) fmt.Println(*a) } ``` #####12.pinic和recover ``` package main import "fmt" func f1(){ defer func(){ // recover err :=recover()//尝试将函数从当前异常状态恢复过来 fmt.Println("recover抓到了panic异常",err) }() var a []int a[0]=100 //panic } func main() { f1() fmt.Println("这是main函数,不执行说明没有运行到这里") } ``` #####13.自定义类型 ``` package main import "fmt" //自定义类型 type NewInt int //类型别名 type MyInt = int func main() { var a NewInt fmt.Println(a) fmt.Printf("type:%T\n",a) var b MyInt fmt.Println(b) fmt.Printf("type:%T\n",b) } ``` #####14.结构体 Go语言中的基础数据类型可以表示一些事物的基本属性,但是当我们想表达一个事物的全部或部分属性时,这时候再用单一的基本数据类型明显就无法满足需求了,Go语言提供了一种自定义数据类型,可以封装多个基本数据类型,这种数据类型叫结构体,英文名称struct。 也就是我们可以通过struct来定义自己的类型了。 Go语言中通过struct来实现面向对象(只支持封装,不支持继承和多态)。 ``` type person struct { name string city string age int8 } func main() { var p1 person p1.name = "沙河娜扎" p1.city = "北京" p1.age = 18 fmt.Printf("p1=%v\n", p1) //p1={沙河娜扎 北京 18} fmt.Printf("p1=%#v\n", p1) //p1=main.person{name:"沙河娜扎", city:"北京", age:18} } ``` Go语言中的方法(Method)是一种作用于特定类型变量的函数。这种特定类型变量叫做接收者(Receiver)。接收者的概念就类似于其他语言中的this或者 self。 ``` //Person 结构体 type Person struct { name string age int8 } //NewPerson 构造函数 func NewPerson(name string, age int8) *Person { return &Person{ name: name, age: age, } } //Dream Person做梦的方法 func (p Person) Dream() { fmt.Printf("%s的梦想是学好Go语言!\n", p.name) } func main() { p1 := NewPerson("小王子", 25) p1.Dream() } ``` 结构体嵌套 ``` package main import "fmt" //结构体嵌套 type address struct{ province string city string } type student struct{ name string age int address //嵌套了一层结构体 } //类型别名 type MyInt = int func main() { var stu1=student{ name:"C航", age:18, address:address{ province:"山东", city:"东营", }, } fmt.Println(stu1.name) } ``` #####15.Json ``` //Student 学生 type Student struct { ID int Gender string Name string } //Class 班级 type Class struct { Title string Students []*Student } func main() { c := &Class{ Title: "101", Students: make([]*Student, 0, 200), } for i := 0; i < 10; i++ { stu := &Student{ Name: fmt.Sprintf("stu%02d", i), Gender: "男", ID: i, } c.Students = append(c.Students, stu) } //JSON序列化:结构体-->JSON格式的字符串 data, err := json.Marshal(c) if err != nil { fmt.Println("json marshal failed") return } fmt.Printf("json:%s\n", data) //JSON反序列化:JSON格式的字符串-->结构体 str := `{"Title":"101","Students":[{"ID":0,"Gender":"男","Name":"stu00"},{"ID":1,"Gender":"男","Name":"stu01"},{"ID":2,"Gender":"男","Name":"stu02"},{"ID":3,"Gender":"男","Name":"stu03"},{"ID":4,"Gender":"男","Name":"stu04"},{"ID":5,"Gender":"男","Name":"stu05"},{"ID":6,"Gender":"男","Name":"stu06"},{"ID":7,"Gender":"男","Name":"stu07"},{"ID":8,"Gender":"男","Name":"stu08"},{"ID":9,"Gender":"男","Name":"stu09"}]}` c1 := &Class{} err = json.Unmarshal([]byte(str), c1) if err != nil { fmt.Println("json unmarshal failed!") return } fmt.Printf("%#v\n", c1) } ``` #####16.包和封装 命名:一般采用全拼首字母大写形式 首字母大写:public 首字母小写:private 每个目录一个包,main包中含有可执行入口main 包(package)是多个Go源码的集合,是一种高级的代码复用方案,Go语言为我们提供了很多内置包,如fmt、os、io等。 要在代码中引用其他包的内容,需要使用import关键字导入使用的包。具体语法如下: ``` import ( "fmt" m "github.com/Q1mi/studygo/pkg_test" ) func main() { fmt.Println(m.Add(100, 200)) fmt.Println(m.Mode) } ``` time包 time.Time类型表示时间。我们可以通过time.Now()函数获取当前的时间对象,然后获取时间对象的年月日时分秒等信息。示例代码如下: ``` func timeDemo() { now := time.Now() //获取当前时间 fmt.Printf("current time:%v\n", now) year := now.Year() //年 month := now.Month() //月 day := now.Day() //日 hour := now.Hour() //小时 minute := now.Minute() //分钟 second := now.Second() //秒 fmt.Printf("%d-%02d-%02d %02d:%02d:%02d\n", year, month, day, hour, minute, second) } ``` 线程同步,由于资源是共享的所以需要加锁 1.互斥锁,var mu sync.Mutex 2.读写锁,var mu sync.RWMutex 闭包 ```go package main import "fmt" func Adder() func(int) int{ var x int return func(i int) int { x+=i return x } } func main(){ f:=Adder() fmt.Println(f(1)) fmt.Println(f(100)) fmt.Println(f(1000)) } /* 结果 1 101 1101 */ ``` #####17.接口 接口(interface)定义了一个对象的行为规范,只定义规范不实现,由具体的对象来实现规范的细节。 在Go语言中接口(interface)是一种类型,一种抽象的类型。 interface是一组method的集合,是duck-type programming的一种体现。接口做的事情就像是定义一个协议(规则),只要一台机器有洗衣服和甩干的功能,我就称它为洗衣机。不关心属性(数据),只关心行为(方法)。 为了保护你的Go语言职业生涯,请牢记接口(interface)是一种类型。 ``` // Sayer 接口 type Sayer interface { say() } type dog struct {} type cat struct {} // dog实现了Sayer接口 func (d dog) say() { fmt.Println("汪汪汪") } // cat实现了Sayer接口 func (c cat) say() { fmt.Println("喵喵喵") } ``` ``` package main import "fmt" // Animaler 接口 type Animaler interface { speak() move() } type cat struct { name string } // cat实现了Animaler接口 func (c cat) speak(){ fmt.Println("喵喵喵") } func (c cat) move(){ fmt.Println("猫会动") } func main() { var x Animaler hh:=cat{"小猫hh"}// cat x=hh tom:=&cat{"汤姆猫tom"}// *cat x=tom tom.speak() tom.move() fmt.Println(x) } ``` #####18.文件 ```` package main import ( "bufio" "fmt" "io" "os" ) // bufio按行读取示例 func main() { file, err := os.Open("./xx.txt") if err != nil { fmt.Println("open file failed, err:", err) return } defer file.Close() reader := bufio.NewReader(file) for { line, err := reader.ReadString('\n') //注意是字符 if err == io.EOF { if len(line) != 0 { fmt.Println(line) } fmt.Println("文件读完了") break } if err != nil { fmt.Println("read file failed, err:", err) return } fmt.Print(line) } } ```` #####19.反射 不知道是什么类型,但是可以通过判断动态确定要走的路径。 低效的,但是更灵活。 反射是一个强大并富有表现力的工具,能让我们写出更灵活的代码。但是反射不应该被滥用,原因有以下三个。 >基于反射的代码是极其脆弱的,反射中的类型错误会在真正运行的时候才会引发panic,那很可能是在代码写完的很长时间之后。 大量使用反射的代码通常难以理解。 反射的性能低下,基于反射实现的代码通常比正常代码运行速度慢一到两个数量级。 什么是反射? >反射是指在程序运行期对程序本身进行访问和修改的能力。程序在编译时,变量被转换为内存地址,变量名不会被编译器写入到可执行部分。在运行程序时,程序无法获取自身的信息。 支持反射的语言可以在程序编译期将变量的反射信息,如字段名称、类型信息、结构体信息等整合到可执行文件中,并给程序提供接口访问反射信息,这样就可以在程序运行期获取类型的反射信息,并且有能力修改它们。 Go程序在运行期使用reflect包访问程序的反射信息。 在上一篇博客中我们介绍了空接口。 空接口可以存储任意类型的变量,那我们如何知道这个空接口保存的数据是什么呢? 反射就是在运行时动态的获取一个变量的类型信息和值信息。 #####20.并发 在java/c++中我们要实现并发编程的时候,我们通常需要自己维护一个线程池,并且需要自己去包装一个又一个的任务,同时需要自己去调度线程执行任务并维护上下文切换,这一切通常会耗费程序员大量的心智。那么能不能有一种机制,程序员只需要定义很多个任务,让系统去帮助我们把这些任务分配到CPU上实现并发执行呢? Go语言中的goroutine就是这样一种机制,goroutine的概念类似于线程,但 goroutine是由Go的运行时(runtime)调度和管理的。Go程序会智能地将 goroutine 中的任务合理地分配给每个CPU。Go语言之所以被称为现代化的编程语言,就是因为它在语言层面已经内置了调度和上下文切换的机制。 在Go语言编程中你不需要去自己写进程、线程、协程,你的技能包里只有一个技能–goroutine,当你需要让某个任务并发执行的时候,你只需要把这个任务包装成一个函数,开启一个goroutine去执行这个函数就可以了,就是这么简单粗暴。 1.并发和并行 >并行:同一时刻在做多种事情。 并发:同一时间段同时在做多种事情。 2.进程、线程、协程 >进程:一个程序启动后创建一个进程。 线程:操作系统调度的最小单位。 协程:用户态的线程(PHP/Python)。 3.goroutine<->coroutine(协程) ``` package main import ( "fmt" "time" ) func main() { for i := 0; i < 10; i++ { go func(i int) { for { fmt.Printf("Hello from goroutine %d \n", i) } }(i) //都有i不安全,所以传入 } time.Sleep(time.Microsecond) } ``` 4.channel 内建函数 make 用来为 slice,map 或 chan 类型分配内存和初始化一个对象(注意:只能用在这三种类型上),跟 new 类似,第一个参数也是一个类型而不是一个值,跟 new 不同的是,make 返回类型的引用而不是指针,而返回值也依赖于具体传入的类型。 ``` package main import ( "fmt" "time" ) func chanDemo() { c := make(chan int) go func() { for { n := <-c //channel收数据 fmt.Println(n) } }() c <- 1 //channel发数据 c <- 2 time.Sleep(time.Microsecond) } func main() { chanDemo() } ``` 把channel作为参数进行使用调度 ``` package main import ( "fmt" "time" ) func worker(id int, c chan int) { for { n := <-c //channel收数据 fmt.Printf("worker %d received %c\n", id, n) } } func chanDemo() { var channels [10]chan int for i := 0; i < 10; i++ { channels[i] = make(chan int) go worker(i, channels[i]) } for i := 0; i < 10; i++ { channels[i] <- 'a' + i } for i := 0; i < 10; i++ { channels[i] <- 'A' + i } time.Sleep(time.Microsecond) } func main() { chanDemo() } ``` 调度器channel ``` package main import ( "fmt" "time" ) func worker(id int, c chan int) { for n := range c { // n, ok := <-c // if !ok { // break // } fmt.Printf("worker %d received %d\n", id, n) } } func createWorker(id int) chan int { c := make(chan int) go func() { for { n := <-c //channel收数据 fmt.Printf("worker %d received %c\n", id, n) } }() 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.Microsecond) } func bufferedChannel() { c := make(chan int, 3) go worker(0, c) c <- 'a' c <- 'b' c <- 'c' c <- 'd' time.Sleep(time.Millisecond) } func channelClose() { c := make(chan int, 3) go worker(0, c) c <- 'a' c <- 'b' c <- 'c' c <- 'd' close(c) time.Sleep(time.Millisecond) } func main() { //channel作为参数 //chanDemo() //bufferedChannel() //理论基础:Communiction Sequential Process(CSP) channelClose() } ``` 使用等待任务监听channel结束 ``` 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 done chan bool } func createWorker(id int) worker { w := worker{ in: make(chan int), done: make(chan bool), } go func() { go doWork(id, w.in, w.done) }() return w } 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 //<-worker.done //这样会顺序打印 } for i, worker := range workers { worker.in <- 'A' + i //<-worker.done } // wait for all of them for _, worker := range workers { <-worker.done <-worker.done } } func main() { chanDemo() } ``` 使用waitgroup处理多任务 ``` package main import ( "fmt" "sync" ) func doWork(id int, c chan int, wg *sync.WaitGroup) { for n := range c { fmt.Printf("worker %d received %c\n", id, n) wg.Done() } } type worker struct { in chan int wg *sync.WaitGroup } func createWorker(id int, wg *sync.WaitGroup) worker { w := worker{ in: make(chan int), wg: wg, } go func() { go doWork(id, w.in, wg) }() return w } func chanDemo() { var wg sync.WaitGroup var workers [10]worker 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() } ``` 使用waitgroup处理多任务使用函数式编程包装 ``` 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) w.done() } } type worker struct { in chan int done func() } func createWorker(id int, wg *sync.WaitGroup) worker { w := worker{ in: make(chan int), done: func() { wg.Done() }, } go func() { go doWork(id, w) }() return w } func chanDemo() { var wg sync.WaitGroup var workers [10]worker 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() } ``` select对channel进行调度 ``` package main import ( "fmt" "math/rand" "time" ) func generator() chan int { out := make(chan int) go func() { i := 0 for { time.Sleep(time.Duration(rand.Intn(1500)) * time.Millisecond) out <- i i++ } }() return out } func main() { var c1, c2 = generator(), generator() for { select { case n := <-c1: fmt.Println("Received from c1:", n) case n := <-c2: fmt.Println("Received from c2:", n) // default: // fmt.Println("No value received") } } } ``` 控制消息排队拿取 ``` package main import ( "fmt" "math/rand" "time" ) 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 generator() chan int { out := make(chan int) go func() { i := 0 for { time.Sleep(time.Duration(rand.Intn(1500)) * time.Millisecond) out <- i i++ } }() return out } func main() { var c1, c2 = generator(), generator() var worker = createWorker(0) var values []int tm := time.After(10 * time.Second) //10秒钟结束 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 <-tick: fmt.Printf("data len %d \n", len(values)) case <-tm: fmt.Println("go bye") return } } } ``` 同步使用并验证 一般并发的bug 有两种,死锁(block)和 竞争(race) 死锁发生时,`go run` 会直接报错 `race`发生时,要加`race` 才会在运行时报warning `go run xxx.go` 后面加上 `-race` 参数 ``` package main import ( "fmt" "time" ) type atomicInt int func (a *atomicInt) increment() { *a++ } func (a *atomicInt) get() int { return int(*a) } func main() { var a atomicInt a.increment() go func() { a.increment() }() time.Sleep(time.Second) fmt.Println(a) } ``` #####21.异常处理 defer保证执行 ``` package main import ( "fmt" ) func testedefer() { defer fmt.Println(1) defer fmt.Println(2) fmt.Println(3)//输出3,2,1 } func main() { testedefer() } ``` 错误处理 ``` package main import ( "bufio" "fmt" "os" ) func fibonacci() func() int { a, b := 0, 1 return func() int { a, b = b, a+b return a } } func writeFile(filename string) { file, err := os.OpenFile( filename, os.O_EXCL|os.O_CREATE, 0666) if err != nil { if pathError, ok := err.(*os.PathError); !ok { panic(err) } else { fmt.Printf("%s,%s,%s\n", pathError.Op, pathError.Path, pathError.Err) } return } defer file.Close() write := bufio.NewWriter(file) defer write.Flush() f := fibonacci() for i := 0; i < 20; i++ { fmt.Println(write, f()) } } func main() { writeFile("1.txt") } ``` #####22.http 简单的文件服务器 ``` package main import ( "fmt" "io/ioutil" "net/http" _ "net/http/pprof"//http://127.0.0.1:8888/debug/pprof/ "os" ) func main() { fmt.Println("开始") http.HandleFunc("/list/", func(writer http.ResponseWriter, request *http.Request) { path := request.URL.Path[len("/list/"):] file, err := os.Open(path) if err != nil { http.Error(writer, err.Error(), http.StatusInternalServerError) return } defer file.Close() all, err := ioutil.ReadAll(file) if err != nil { panic(err) } writer.Write(all) }) http.Handle("/", http.FileServer(http.Dir("."))) err := http.ListenAndServe(":8888", nil) if err != nil { panic(err) } } ``` 获取手机版 ``` package main import ( "fmt" "net/http" "net/http/httputil" ) func main() { request, err := http.NewRequest( http.MethodGet, "http://www.imooc.com", nil) request.Header.Add("User-Agent", "Mozilla/5.0 (Linux; Android 6.0; Nexus 5 Build/MRA58N) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/83.0.4103.61 Mobile Safari/537.36") resp, err := http.DefaultClient.Do(request) //http.Get("http://www.imooc.com") if err != nil { panic(err) } defer resp.Body.Close() s, err := httputil.DumpResponse(resp, true) if err != nil { panic(err) } fmt.Printf("%s\n", s) } ``` ####注意事项
上一篇
下一篇
版权所有:机遇屋在线 Copyright © 2021-2025 jiyuwu Co., Ltd.
鲁ICP备16042261号-1