当前位置: 首页 > news >正文

苏州企业网站制作多少钱做网站站怎么赚钱吗

苏州企业网站制作多少钱,做网站站怎么赚钱吗,主机托管服务,wordpress 修改语言目录 泛型内置泛型的使用切片泛型和泛型函数map泛型泛型约束泛型完整代码 接口反射协程特点WaitGroupgoroutine的调度模型#xff1a;MPG模型 channel介绍语法#xff1a;举例#xff1a;channel遍历基本使用和协程一起使用案例一案例二 select...casemain.go 完整代码 文件… 目录 泛型内置泛型的使用切片泛型和泛型函数map泛型泛型约束泛型完整代码 接口反射协程特点WaitGroupgoroutine的调度模型MPG模型 channel介绍语法举例channel遍历基本使用和协程一起使用案例一案例二 select...casemain.go 完整代码 文件 go往期文章笔记 【Java转Go】快速上手学习笔记一之环境安装篇 【Java转Go】快速上手学习笔记二之基础篇一 【Java转Go】快速上手学习笔记三之基础篇二 这篇主要是泛型、接口、反射、协程、管道、文件操作的笔记因为我的笔记都是按照视频说的来记的可能大家光看的话会有些看不明白所以建议大家可以把代码复制下来配合里面的注释一起自己去运行一遍加深理解 泛型 定义泛型func 函数名 [泛型参数类型] (函数参数) {} 内置泛型的使用 Go内置的两个泛型any 和 comparable any表示go里面所有的内置基本类型等价于 interface{}comparable表示go里面所有内置的可比较类型int、uint、float、booi.struct、指针 等一切可以比较的类型 func printArr[T any](arr []T) {for _, item : range arr {fmt.Println(item)} }// 泛型的使用 func 泛型的基本使用() {strArr : []string{白夜, 炽翎}intArr : []int{1, 2}printArr(strArr)printArr(intArr) }切片泛型和泛型函数 // 自定义一个切片泛型 type mySlice[T int | float64] []T// 给自定义的切片泛型添加一个求和方法 func (s mySlice[T]) sum() T {var sum Tfor _, v : range s {sum v}return sum }// 定义一个泛型函数 func add[T int | float64 | float32 | string](a T, b T) T {return a b }// 泛型函数与方法 func 切片泛型的使用() {// 往自定义的切片泛型里添加int类型的值var i mySlice[int] []int{1, 2, 3, 4}fmt.Println(i.sum()) // 可以直接调用为切片泛型添加的一个求和方法// 往自定义的切片泛型里添加float64类型的值var f mySlice[float64] []float64{1.5, 2.7, 3.89, 4.55}fmt.Println(f.sum())//fmt.Println(add[int](1, 2))fmt.Println(add(1, 2)) // 调用时可以自动推导传入的参数的类型//fmt.Println(add[string](hh, 66))fmt.Println(add(hh, 66))//fmt.Println(add[float64](1.6, 2.8))fmt.Println(add(1.6, 2.8)) }map泛型 // 泛型map type myMap[K string | int, V any] map[K]V type User struct {Name string }func map泛型的使用() {m1 : myMap[string, string]{key: 符华,}fmt.Println(m1)m2 : myMap[int, User]{0: User{符华},}fmt.Println(m2) }泛型约束 // 自定义一个类型别名将int8类型设置一个别名 type int8A int8// 自定义一个泛型约束 type myInt interface {// 当类型设置了别名在使用的时候要么在后面把这个别名约束也加进去//int | int8 | int16 | int32 | int64 | int8A// 要么在这个类型前面加一个 ~ 符号因为类型的别名是这个类型的衍生类型在类型前面加 ~ 号就可以适配这个类型的全部衍生类型了int | ~int8 | int16 | int32 | int64 }// 给泛型约束定义一个比较大小的泛型函数 func getMaxNum[T myInt](a, b T) T {if a b {return a}return b }func 泛型约束的使用() {//var a int 10var a int8A 10//var b int 20var b int8A 20fmt.Println(getMaxNum(a, b)) }泛型完整代码 package mainimport fmt/* 泛型内置的泛型类型 any 和 comparable any表示go里面所有的内置基本类型等价于 interface{} comparable表示go里面所有内置的可比较类型int、uint、float、booi.struct、指针 等一切可以比较的类型 */ func printArr[T any](arr []T) {for _, item : range arr {fmt.Println(item)} }// 自定义一个切片泛型 type mySlice[T int | float64] []T// 给自定义的切片泛型添加一个求和方法 func (s mySlice[T]) sum() T {var sum Tfor _, v : range s {sum v}return sum }// 泛型函数 func add[T int | float64 | float32 | string](a T, b T) T {return a b }// 泛型map type myMap[K string | int, V any] map[K]V type User struct {Name string }// 自定义一个类型别名将int8类型设置一个别名 type int8A int8// 自定义一个泛型约束 type myInt interface {// 当类型设置了别名在使用的时候要么在后面把这个别名约束也加进去//int | int8 | int16 | int32 | int64 | int8A// 要么在这个类型前面加一个 ~ 符号因为类型的别名是这个类型的衍生类型在类型前面加 ~ 号就可以适配这个类型的全部衍生类型了int | ~int8 | int16 | int32 | int64 }// 给泛型约束定义一个比较大小的泛型函数 func getMaxNum[T myInt](a, b T) T {if a b {return a}return b }func main() {//泛型的基本使用()//切片泛型的使用()//map泛型的使用()泛型约束的使用() }// 泛型的使用 func 泛型的基本使用() {strArr : []string{白夜, 炽翎}intArr : []int{1, 2}printArr(strArr)printArr(intArr) }// 泛型函数与方法 func 切片泛型的使用() {// 往自定义的切片泛型里添加int类型的值var i mySlice[int] []int{1, 2, 3, 4}fmt.Println(i.sum()) // 可以直接调用为切片泛型添加的一个求和方法// 往自定义的切片泛型里添加float64类型的值var f mySlice[float64] []float64{1.5, 2.7, 3.89, 4.55}fmt.Println(f.sum())//fmt.Println(add[int](1, 2))fmt.Println(add(1, 2)) // 调用时可以自动推导传入的参数的类型//fmt.Println(add[string](hh, 66))fmt.Println(add(hh, 66))//fmt.Println(add[float64](1.6, 2.8))fmt.Println(add(1.6, 2.8)) }func map泛型的使用() {m1 : myMap[string, string]{key: 符华,}fmt.Println(m1)m2 : myMap[int, User]{0: User{符华},}fmt.Println(m2) }func 泛型约束的使用() {//var a int 10var a int8A 10//var b int 20var b int8A 20fmt.Println(getMaxNum(a, b)) }接口 接口用 type 和 interface 关键字定义 定义格式 type 接口名 interface {接口方法1(参数1 参数类型.....) [返回类型]接口方法2() [返回类型]接口方法3()...接口方法n() [返回类型] }接口可以将不同的类型绑定到一组公共的方法上从而实现多态。提高代码的复用率 Go中的接口是隐式实现的也就是说如果一个类型实现了一个接口定义的所有方法那么它就自动地实现了该接口。不用像Java一样用implements关键字指定实现哪个接口 因此我们可以通过将接口作为参数来实现对不同类型的调用从而实现多态。 // 定义一个 寸劲 接口 type 寸劲 interface {// 这个接口里面有这几个方法寸劲开天(days int) string // 有参数有返回值的方法寸劲山崩() string // 无参数有返回值的方法寸劲岩破() // 无参数无返回值的方法 }// 定义一个 太虚剑气 接口 type 太虚剑气 interface {太虚剑神(days int) string }// 定义一个函数以空接口作为参数可以传任何类型的参数 func dataPrint(datas ...interface{}) {for i, x : range datas {switch x.(type) {case bool:fmt.Printf(参数 #%d 是一个bool类型它的值是%v\n, i, x)case string:fmt.Printf(参数 #%d 是一个string类型它的值是%v\n, i, x)case int:fmt.Printf(参数 #%d 是一个int类型它的值是%v\n, i, x)case float64:fmt.Printf(参数 #%d 是一个float64类型它的值是%v\n, i, x)case nil:fmt.Printf(参数 #%d 是一个nil类型它的值是%v\n, i, x)default:fmt.Printf(参数 #%d 是其他类型它的值是%v\n, i, x)}} }// 定义一个用户学习结构体来实现接口所有个方法一个类型实现了接口的所有方法即实现了该接口 type 学习 struct {name string }// 定义一个结构体特有的方法 func (x 学习) 开始学习() string {return fmt.Sprint(x.name, 现在要开始学习了.....) }// 实现 寸劲开天 接口这里也可以用指针 x *学习用了指针后那么赋值的时候也需要传指针类型学习{符华} func (x *学习) 寸劲开天(days int) string {return fmt.Sprint(x.name, 学了, days, 天学完了寸劲开天) }// 实现 寸劲山崩 接口 func (x 学习) 寸劲山崩() string {return fmt.Sprint(x.name, 学完了寸劲山崩) }// 实现 寸劲岩破 接口 func (x 学习) 寸劲岩破() {fmt.Println(x.name, 学完了寸劲岩破) }// 实现 太虚剑神 接口 func (x *学习) 太虚剑神(days int) string {return fmt.Sprint(x.name, 学了, days, 天学完了太虚剑神) }func main() {接口的使用() }func 接口的使用() {u : 学习{符华}var cj 寸劲//cj u // 接口赋值为 学习 结构体只有当实现了接口的全部方法才能赋值给接口否则无法赋值cj u // 只要接口方法有一个指针实现则此处必须是指针if u1, ok : cj.(*学习); ok { // 通过类型断言来调用 结构体 独有的方法fmt.Println(u1.开始学习())}cj.寸劲岩破()fmt.Println(cj.寸劲山崩())fmt.Println(cj.寸劲开天(2))/*类型断言由于接口是一般类型不知道具体类型如果要转成具体类型就需要使用类型断言语法接口.(类型)类型不是什么类型都可以传必须要 接口 原先指向什么类型那么就传什么类型返回两个值可以通过返回的 true、false 来判断断言转换是否成功*///var jq 太虚剑气//jq, ok : cj.(太虚剑气)if jq, ok : cj.(太虚剑气); ok { // 如果转换成功ok为truefmt.Println(jq.太虚剑神(10))} else {fmt.Println(转换失败)}var a interface{}a u // 将 u 赋值给a然后将 a 重新赋值给一个 学习 类型的变量这就需要用 类型断言var u1 学习//u1 a // 这里不可以直接赋值需要使用类型断言u1 a.(学习) // a 原先指向 学习 类型所以传类型时也必须要传 学习 类型fmt.Println(u1)// 空接口dataPrint(u, 空接口, 123, 12.65, []int{1, 2, 3}, make(map[string]string, 2)) }反射 Go中使用反射需要导入 reflect 包 使用反射时主要有两个很重要的方法 reflect.TypeOf(变量名)获取变量的类型返回 reflect.Type 类型是一个接口reflect.ValueOf(变量名)获取变量的值返回 reflect.Value 类型是一个结构体类型 变量、interface{} 和 reflect.Value 是可以相互转换的如下图 package mainimport (fmtreflect )/* 反射需要导入 reflect 包 主要有两个函数reflect.TypeOf(变量名)获取变量的类型返回 reflect.Type 类型是一个接口reflect.ValueOf(变量名)获取变量的值返回 reflect.Value 类型是一个结构体类型变量、interface{}和 reflect.Value 是可以相互转换的 */type student struct {Name string json:nameAge int }// 给 student 结构体绑定方法 func (s student) PrintStu() {fmt.Println(s) } func (s student) GetSum(a, b int) {fmt.Println(a b) }// 基本数据类型、interface{}、reflect.Value 相互转换 func reflectTest01(a interface{}) {// 通过反射获取传入的变量的 typerTyp : reflect.TypeOf(a)fmt.Println(rTyp, rTyp)// 获取到 reflect.ValuerVal : reflect.ValueOf(a)n1 : 10 rVal.Int() // 通过反射来获取变量的值要求数据类型匹配reflect.Value.Int()、reflect.Value.String()、reflect.Value.Float()......fmt.Println(n1)fmt.Printf(rVal%v rVal的类型%T\n, rVal, rVal)// 将 reflect.Value 转回 interface{}iV : rVal.Interface()// 将 interface{} 通过断言转回 需要的类型n2 : iV.(int)fmt.Println(n2) }// 对结构体的反射 func reflectTest02(a interface{}) {// 通过反射获取传入的变量的 typerTyp : reflect.TypeOf(a)fmt.Println(rTyp, rTyp)// 获取到 reflect.ValuerVal : reflect.ValueOf(a)fmt.Printf(rVal%v rVal的类型%T\n, rVal, rVal)fmt.Println(kind, rVal.Kind(), rTyp.Kind())// 将 reflect.Value 转回 interface{}iV : rVal.Interface()// 通过反射来获取结构体的值需要先断言// 将 interface{} 通过断言转回 需要的类型stu : iV.(student)fmt.Println(stu) }// 通过反射改变值必须传入指针才能改变值 func reflectTest03(a interface{}) {rTyp : reflect.TypeOf(a) // 通过反射获取传入的变量的 typefmt.Println(rTyp, rTyp)rVal : reflect.ValueOf(a) // 获取到 reflect.Valueswitch a.(type) { // 判断传入的参数的类型case *int:n1 : 10 rVal.Elem().Int() // 通过反射来获取变量的值因为传入的是指针所以要先用 Elem() 再获取值fmt.Println(n1)fmt.Printf(rVal%v rVal的类型%T\n, rVal.Elem(), rVal)rVal.Elem().SetInt(200) // 通过反射改变值case *student:e : rVal.Elem()e.FieldByName(Name).SetString(白夜) // 给指定的字段名改变值} }// 通过反射遍历结构体的方法和属性 func reflectTest04(a interface{}) {rTyp : reflect.TypeOf(a)if rTyp.Kind() ! reflect.Struct { // 判断传入的参数是否是结构体return}rVal : reflect.ValueOf(a)// 遍历结构体字段numField : rTyp.NumField() // 获取结构体字段的数量fmt.Println(numField , numField)for i : 0; i numField; i {// 打印字段的类型、字段名、字段值、字段标签fmt.Println(rTyp.Field(i).Type, rTyp.Field(i).Name, , rVal.Field(i), rTyp.Field(i).Tag.Get(json))}// 遍历结构体方法numMethod : rTyp.NumMethod() // 获取结构体方法的数量// 关于方法遍历时方法的索引是根据方法名称的ACSII码来排序的for i : 0; i numMethod; i {// 打印方法的类型、方法名//fmt.Println(rTyp.Method(i).Type, rTyp.Method(i).Name)if i 0 {var params []reflect.Valueparams append(params, reflect.ValueOf(10))params append(params, reflect.ValueOf(20))rVal.Method(i).Call(params)} else {rVal.Method(i).Call(nil)}} }func main() {// 基本数据类型、interface{}、reflect.Value 相互转换//var num int 100//reflectTest01(num)//reflectTest03(num) // 修改值必须传指针//fmt.Println(通过反射改变num的值, num)stu : student{符华, 20}//reflectTest02(stu)//reflectTest03(stu) // 修改值必须传指针//fmt.Println(通过反射改变stu的值, stu)reflectTest04(stu) }协程 接下来我们讲协程 协程一个进程有多个线程一个线程可以起多个协程 特点 有独立的栈空间共享程序堆空间调度由用户控制协程是轻量级的线程 主线程结束后协程会被中断这时需要一个有效的阻塞机制。 WaitGroup 如果主线程退出了即使协程还没有执行完毕也会退出。这时我们可以使用WaitGroup它用于等待一组协程的结束。 父线程调用Add方法来设定应等待的协程的数量。每个被等待的协程在结束时应调用Done方法。同时主线程里可以调用Wait方法阻塞至所有协程结束。 goroutine的调度模型MPG模型 M操作系统的主线程是物理线程P协程执行需要的上下文G协程 使用 goroutine 效率高但是会出现并发/并行安全问题需要加锁解决这个问题。 如果协程发生异常可以用recover来捕获异常进行除了。这样主函数不会受到影响可以继续执行。 package mainimport (fmtstrconvsynctime )// 一个函数每隔1秒输出 func goroutineTest01() {for i : 0; i 10; i {fmt.Println(test() hello,world strconv.Itoa(i))time.Sleep(time.Second)} }var (myMap make(map[int]int, 10)// 定义一个全局的互斥锁lock sync.Mutex // sync 同步的意思Mutex 互斥的意思wg sync.WaitGroup // 用于等待一组线程的结束 )func goroutineTest02(n int) {res : 1for i : 1; i n; i {res * i}lock.Lock() // 写之前加锁myMap[n] res // concurrent map writes 并发写入问题lock.Unlock() // 写完之后解锁//wg.Add()中有20个需要执行的协程每执行完一个后调用wg.Done()让协程数量-1直到协程数量为0表示全部协程执行完毕wg.Done() // 这里表示每执行完一个协程wg.Add()里面的数量-1 }func main() {协程() }func 协程() {//goroutineTest01() // 如果这样调用这里是先执行完goroutineTest01再执行main里面的打印//go goroutineTest01() // 开启了一个线程这样goroutineTest01和main就是同时执行//for i : 0; i 10; i {// fmt.Println(main() hello,world strconv.Itoa(i))// time.Sleep(time.Second)//}//cpuNum : runtime.NumCPU() //获取电脑的cpu数量//fmt.Println(cpu个数, cpuNum)// 可以自己设置使用多少个cpu//runtime.GOMAXPROCS(cpuNum - 1) // 预留一个cpuwg.Add(20) // 这里表示有20个协程需要执行// 开启多个协程for i : 1; i 20; i {go goroutineTest02(i)}wg.Wait() // 告诉主线程要等一下等协程全部执行完了载退出fmt.Println(全部协程执行完毕)// 遍历输出map结果for i, v : range myMap {fmt.Printf(map[%d]%d\n, i, v)} }func 协程异常捕获() {// 这里我们可以使用defer recover来捕获异常defer func() {if err : recover(); err ! nil {fmt.Println(发生错误错误信息, err)}}()var myMap map[int]stringmyMap[0] Go // map没有make出现error } channel channel也就是管道一般情况下我们是配合协程一起使用的。 channel管道本质就是一个数据结构——队列 介绍 数据是先进先出FIFOfirst in first out线程安全多goroutine访问时不需要加锁就是说channel本身就是线程安全的channel是有类型的一个string的channel只能存放string类型数据channel必须是引用类型必须初始化才能写入数据也就是需要make后才能使用 语法 var 变量名 chan 数据类型变量名 make(chan 数据类型, 容量) 使用make进行初始化 举例 var intChan chan int // 用于存放int数据 var mapChan chan map[int]string // 用于存放map[int]string数据 var perChan chan Pserson // 用于存放Pserson结构体数据 var perChan1 chan *Pserson //用于存放Pserson结构体指针数据 var perChan1 chan interface{} //可以存放任何类型数据但是取的时候要注意用类型断言channel遍历 通常使用for-range方式进行遍历不用取长度的方式来遍历管道是因为管道每取一次长度就会变。 在遍历时如果管道没有关闭会出现deadlock的错误如果管道已经关闭则正常遍历数据遍历完后退出遍历。 管道可以声明为只读或只写默认情况下是双向的也就是可读可写 只写var intChan chan- int只读var intChan -chan int 基本使用 func 管道() {// 创建一个可以存放3个int类型的管道var intChan chan int// 因为channel是引用类型它的值其实是一个地址然后这个地址指向的就是管道队列然后intChan本身也有一个地址intChan make(chan int, 3)fmt.Printf(intChan 的值%v intChan 本身的地址%p\n, intChan, intChan) // intChan 的值0xc00006e080 intChan 本身的地址0xc00004c020// 向管道写入数据写入、读取管道数据时用 - 表达式intChan - 10num : 200intChan - num// 设置的管道容量是3最多只能往里面写入3条数据长度不能超过容量intChan - 100// 管道的长度和cap容量fmt.Printf(管道 长度 %v 容量 %v\n, len(intChan), cap(intChan)) // 3,3// 读取管道的数据。从管道中取出了数据可以再往里面放数据//-intChan // 可以直接这么写也是取出数据不用变量接收把取出的数据扔了不要n1 : -intChan // 这里取出来的是最先写入到管道里的数据先进先出fmt.Println(n1, n1) // 10fmt.Printf(管道 长度 %v 容量 %v\n, len(intChan), cap(intChan)) // 2,3// 取出了一条数据后再往里面放一条数据intChan - 500close(intChan) // 关闭管道这时就不能再往管道里面写入数据了但是读取没问题fmt.Printf(管道 长度 %v 容量 %v\n, len(intChan), cap(intChan)) // 3,3// 在没有使用协程的情况下如果管道数据已经全部取出再取会报错n2 : -intChann3 : -intChanfmt.Printf(n2 %v n3 %v\n, n2, n3) // 200,100fmt.Printf(管道 长度 %v 容量 %v\n, len(intChan), cap(intChan)) // 1,3// 遍历管道intChan2 : make(chan int, 100)for i : 0; i 100; i {intChan2 - i * 2}close(intChan2) // 管道写完数据后先将管道关闭再进行遍历// 不能用取长度的方式来遍历管道因为管道每取一次长度就会变要用 range 方式遍历for v : range intChan2 { // 这里只返回一个数据管道里面没有下标fmt.Println(v , v)} }和协程一起使用 案例一 package mainimport (fmtsync )// 全局 WaitGroup 变量 var wg sync.WaitGroup // 用于等待一组线程的结束// 管道写入数据 func writeData(intChan chan int) {for i : 0; i 50; i {intChan - ifmt.Printf(writeData 写入数据%v\n, i)}close(intChan)wg.Done() // 执行完一个线程后调用这个方法主线程中需要等待执行的协程数量-1 }// 管道读取数据 func readData(intChan chan int) {for {v, ok : -intChanif !ok {break}fmt.Printf(readData 读到数据%v\n, v)}wg.Done() // 执行完一个线程后调用这个方法主线程中需要等待执行的协程数量-1 }func main() {协程和管道应用1() }func 协程和管道应用1() {// 创建两个管道intChan : make(chan int, 10)wg.Add(2) // 说明开启了两个线程// 开启了两个协程writeData和readData应该是交叉执行的go writeData(intChan) // 开启一个协程往 intChan 中写入数据go readData(intChan) // 开启一个协程读取 intChan 的数据wg.Wait() // 告诉主线程需要等待协程执行完毕fmt.Println(程序执行完毕) }案例二 需求要求统计1-8000的数字中哪些是素数 将统计素数的任务分配给4个协程去完成 // 判断是否为素数 func isPrime(intChan, primeChan chan int) {var isPrime bool // 标识是否是素数for v : range intChan {isPrime truefor i : 2; i v; i {if v%i 0 {isPrime falsebreak}}if isPrime { // 如果为素数则往primeChan中写入数据primeChan - v}}fmt.Println(isPrime 读取素数完毕)wg.Done() // 执行完一个线程后调用这个方法主线程中需要等待执行的协程数量-1 }func 协程和管道应用2() {// 需求要求统计1-8000的数字中哪些是素数// 将统计素数的任务分配给4个协程去完成intChan : make(chan int, 1000) // 读写1-8000数字的管道primeChan : make(chan int, 2000) // 存储素数的管道wg.Add(5) // 下面开启了5个协程// 开启写入 1-8000 数字的协程go func() {for i : 1; i 8000; i {intChan - i}close(intChan)wg.Done()}()// 开启4个读取 1-8000 数字并统计素数的协程for i : 0; i 4; i {go isPrime(intChan, primeChan)}wg.Wait() // 等待协程执行完毕close(primeChan) // 关闭 primeChan 管道// 遍历primeChan把结果取出来for v : range primeChan {fmt.Printf(素数是 %v\n, v)} }select…case 传统的方法在遍历管道时如果不关闭会阻塞而导致 deadlock。 在实际开发中可能不好确定什么时候关闭管道这时可以使用select方式解决。 func 管道注意细节() {intChan : make(chan int, 10)for i : 0; i 10; i {intChan - i}stringChan : make(chan string, 5)for i : 0; i 5; i {stringChan - hello fmt.Sprintf(%d, i)}// 传统的方法在遍历管道时如果不关闭会阻塞而导致 deadlock// 在实际开发中可能不好确定什么时候关闭管道这时可以使用select方式解决for {select {// 这里如果intChan一直没有关闭也不会一直阻塞而导致deadlock// 如果一个case取不到数据会自动到下一个case中取case v : -intChan:fmt.Println(从intChan读取的数据, v)case v : -stringChan:fmt.Println(从stringChan读取的数据, v)default:fmt.Println(都取不到了)return}} }main.go 完整代码 package mainimport (fmtsync )var wg sync.WaitGroup // 用于等待一组线程的结束// 管道写入数据 func writeData(intChan chan int) {for i : 0; i 50; i {intChan - ifmt.Printf(writeData 写入数据%v\n, i)}close(intChan)wg.Done() // 执行完一个线程后调用这个方法主线程中需要等待执行的协程数量-1 }// 管道读取数据 func readData(intChan chan int) {for {v, ok : -intChanif !ok {break}fmt.Printf(readData 读到数据%v\n, v)}wg.Done() // 执行完一个线程后调用这个方法主线程中需要等待执行的协程数量-1 }// 判断是否为素数 func isPrime(intChan, primeChan chan int) {var isPrime bool // 标识是否是素数for v : range intChan {isPrime truefor i : 2; i v; i {if v%i 0 {isPrime falsebreak}}if isPrime { // 如果为素数则往primeChan中写入数据primeChan - v}}fmt.Println(isPrime 读取素数完毕)wg.Done() // 执行完一个线程后调用这个方法主线程中需要等待执行的协程数量-1 }func main() {//管道()//协程和管道应用1()//协程和管道应用2()//管道注意细节() }func 管道() {// 创建一个可以存放3个int类型的管道var intChan chan int// 因为channel是引用类型它的值其实是一个地址然后这个地址指向的就是管道队列然后intChan本身也有一个地址intChan make(chan int, 3)fmt.Printf(intChan 的值%v intChan 本身的地址%p\n, intChan, intChan) // intChan 的值0xc00006e080 intChan 本身的地址0xc00004c020// 向管道写入数据写入、读取管道数据时用 - 表达式intChan - 10num : 200intChan - num// 设置的管道容量是3最多只能往里面写入3条数据长度不能超过容量intChan - 100// 管道的长度和cap容量fmt.Printf(管道 长度 %v 容量 %v\n, len(intChan), cap(intChan)) // 3,3// 读取管道的数据。从管道中取出了数据可以再往里面放数据//-intChan // 可以直接这么写也是取出数据不用变量接收把取出的数据扔了不要n1 : -intChan // 这里取出来的是最先写入到管道里的数据先进先出fmt.Println(n1, n1) // 10fmt.Printf(管道 长度 %v 容量 %v\n, len(intChan), cap(intChan)) // 2,3// 取出了一条数据后再往里面放一条数据intChan - 500close(intChan) // 关闭管道这时就不能再往管道里面写入数据了但是读取没问题fmt.Printf(管道 长度 %v 容量 %v\n, len(intChan), cap(intChan)) // 3,3// 在没有使用协程的情况下如果管道数据已经全部取出再取会报错n2 : -intChann3 : -intChanfmt.Printf(n2 %v n3 %v\n, n2, n3) // 200,100fmt.Printf(管道 长度 %v 容量 %v\n, len(intChan), cap(intChan)) // 1,3// 遍历管道intChan2 : make(chan int, 100)for i : 0; i 100; i {intChan2 - i * 2}close(intChan2) // 管道写完数据后先将管道关闭再进行遍历// 不能用取长度的方式来遍历管道因为管道每取一次长度就会变要用 range 方式遍历for v : range intChan2 { // 这里只返回一个数据管道里面没有下标fmt.Println(v , v)} }func 协程和管道应用1() {// 创建两个管道intChan : make(chan int, 10)wg.Add(2) // 说明开启了两个线程// 开启了两个协程writeData和readData应该是交叉执行的go writeData(intChan) // 开启一个协程往 intChan 中写入数据go readData(intChan) // 开启一个协程读取 intChan 的数据wg.Wait() // 告诉主线程需要等待协程执行完毕fmt.Println(程序执行完毕) }func 协程和管道应用2() {// 需求要求统计1-8000的数字中哪些是素数// 将统计素数的任务分配给4个协程去完成intChan : make(chan int, 1000) // 读写1-8000数字的管道primeChan : make(chan int, 2000) // 存储素数的管道wg.Add(5) // 下面开启了5个协程// 开启写入 1-8000 数字的协程go func() {for i : 1; i 8000; i {intChan - i}close(intChan)wg.Done()}()// 开启4个读取 1-8000 数字并统计素数的协程for i : 0; i 4; i {go isPrime(intChan, primeChan)}wg.Wait() // 等待协程执行完毕close(primeChan) // 关闭 primeChan 管道// 遍历primeChan把结果取出来for v : range primeChan {fmt.Printf(素数是 %v\n, v)} }func 管道注意细节() {intChan : make(chan int, 10)for i : 0; i 10; i {intChan - i}stringChan : make(chan string, 5)for i : 0; i 5; i {stringChan - hello fmt.Sprintf(%d, i)}// 传统的方法在遍历管道时如果不关闭会阻塞而导致 deadlock// 在实际开发中可能不好确定什么时候关闭管道这时可以使用select方式解决for {select {// 这里如果intChan一直没有关闭也不会一直阻塞而导致deadlock// 如果一个case取不到数据会自动到下一个case中取case v : -intChan:fmt.Println(从intChan读取的数据, v)case v : -stringChan:fmt.Println(从stringChan读取的数据, v)default:fmt.Println(都取不到了)return}} }文件 文件这块没啥好说的拿到函数直接用就行。需要注意一点就是文件file是一个指针类型。 package mainimport (bufiofmtioos )func main() {//基本使用读()基本使用写() }func 基本使用读() {// 打开文件file, err : os.Open(C:\\Users\\Administrator\\Desktop\\1.txt)if err ! nil {fmt.Println(文件打开错误, err)}//fmt.Printf(file%v, file) // 输出的是一个地址defer file.Close() // 当函数退出时要关闭file否则会有内存泄露// 创建一个Reader是带缓冲默认缓冲区为4096这种方式比较适合大文件读取reader : bufio.NewReader(file)for {str, err : reader.ReadString(\n)if err io.EOF { // io.EOF表示读到了文件末尾这时就可以退出循环了break}fmt.Print(str)}fmt.Println(文件读取完成)// ioutil.ReaderFile一次性将文件读取到位 这种方法适合读取比较小的文件// 不过新版本 ioutil.ReadFile 已经弃用了这个函数其实调用的就是 os.ReadFilecontent, err : os.ReadFile(C:\\Users\\Administrator\\Desktop\\学习计划.txt)if err ! nil {fmt.Printf(文件读取失败%v, err)}//fmt.Println(content) // content是一个 []bytefmt.Println(string(content)) // 所以要转成string}func 基本使用写() {filePath : C:\\Users\\Administrator\\Desktop\\测试.txt/*OpenFile 第二个参数文件打开模式O_RDONLY int syscall.O_RDONLY // 只读O_WRONLY int syscall.O_WRONLY // 只写O_RDWR int syscall.O_RDWR // 读写O_APPEND int syscall.O_APPEND // 追加O_CREATE int syscall.O_CREAT // 如果不存在就创建O_EXCL int syscall.O_EXCL // 文件必须不存在O_SYNC int syscall.O_SYNC // 同步ioO_TRUNC int syscall.O_TRUNC // 打开时清空文件一般用于覆盖写入第三个参数只作用于linux系统Windows系统不起作用*/file, err : os.OpenFile(filePath, os.O_WRONLY|os.O_CREATE, 0666)if err ! nil {fmt.Printf(文件打开失败%v, err)}defer file.Close()str : hello,Golang\n// NewWriter 带缓冲区的写入写完之后要用flush刷新。writer : bufio.NewWriter(file)for i : 0; i 5; i {writer.WriteString(str)}writer.Flush() } ok以上就是本篇的全部内容了。下一篇可能是关于网络请求相关的也有可能是Gorm相关的。
http://www.hkea.cn/news/14305779/

相关文章:

  • 洛阳网站的优化网页游戏排行nvsheng
  • 企业门户网站的建设费用广州网站建设+致茂
  • 信息网站建设网站建设话语
  • 如何自己建公司网站自己做的网站邮箱更改密码程序为什么总出错
  • 佛山门户网站建设wordpress调用百度网盘视频
  • 玩具网站建设策划书国内网站建设的趋势是怎样的
  • 嘉兴南湖区建设局网站济南seo优化外包服务
  • 北京设计网站建设黑龙江网站备案
  • 怎么找网站站长下载百度免费
  • 做企业网站用什么框架云南做网站报价
  • 徐州市政建设集团公司网站怎么建立自己的销售平台
  • 集团网站开发多少钱网站制作产品资料
  • 群晖做网站域名河南网站建设yijuce
  • 淘宝网站做阳光棚多少钱一平米十大博物馆展陈设计公司
  • 杭州网站定制开发自媒体135软件
  • 北京网站设计联系电话网站原型设计规范
  • 如何通过c语言来做网站做付费下载的网站
  • 婚庆摄影网站模板企业微信网站怎么做的
  • 书画展示网站模板网络营销案例分析
  • 投诉网站怎么做网站的tdk指的是什么
  • 电销做网站的话术深圳做网站哪家公司好
  • 织梦网站手机版怎么做餐馆网站怎么做的
  • 如何做网站ip跳转微信官网电脑版下载
  • 自己建一个网站需要准备什么口碑好的黄石网站建设
  • 廊坊集团网站建设网站建设丨下拉找金手指信誉
  • 自助式网站wordpress 欢迎插件
  • 网站模板下载百度云链接怎么做的纳米加工平台
  • 桂平网站制作中学网站建设工作实施方案
  • 哪个网站可以做行程宜黄县建设局网站
  • 网站建设行业研究最优的赣州网站建设