基于C++和Python基础的Golang学习笔记

文章目录

  • 一、基础
    • 1.DOS命令
    • 2.变量
      • (1)局部变量
      • (2)全局变量
      • (3)数据类型
      • (4)指针
      • (5)运算符
      • (6)自定义数据类型
    • 3.语句
      • (1)条件语句
      • (2)循环语句
    • 4.函数
      • (1)基础
      • (2)结构
  • 二、常用函数
    • 0.基础
      • (1)len() 和 cap()
      • (2)new()
      • (3)make(Type, len, cap)
      • (4)nil
      • (5)append() 和 copy()
      • (6)Range关键字
      • (7)_
      • (8)element()
      • (9).(type)
    • 1.package unsafe
      • (1)unsafe.Sizeof(num)
    • 2.import "fmt"
      • (1)输出
      • (2)输入
    • 3.import "strconv"
    • 4.字符串函数
    • 5.import "os"
    • 6.import "io"
    • 7.import "io/ioutil"
    • 8.import "bufio"
    • 9.import "sync"
  • 三、包及其函数
    • 1.包的引入
    • 2.init函数
    • 3.匿名函数与闭包
    • 4.defer关键字
    • 5.日期和时间函数
    • 6.错误处理
      • (1)错误捕获
      • (2)自定义错误
  • 四、数组、切片、映射
    • 1.数组
    • 2.切片 []
    • 3.集合/字典/映射 Map
  • 五、面向对象
    • 1.结构体
      • (1)基础
      • (2)转换
      • (3)方法的引入
      • (4)方法与函数的差别
      • (5)创建结构体实例
    • 2.封装
    • 3.继承
      • (1)基础
      • (2)注意事项
    • 4.接口
      • (1)基础
      • (2)注意事项
      • (3)多态
    • 6.断言
  • 六、文件操作
    • 1.文件
    • 2.io读取文件
    • 3.io写入文件
    • 4.文件复制操作
  • 七、协程和管道
    • 1.基本概念
      • (1)程序
      • (2)进程
      • (3)线程
      • (4)协程
    • 2.协程
      • (1)多协程
      • (2)延长协程存活
      • (3)互斥锁同步协程
      • (4)读写锁
    • 3.管道
      • (1)管道关闭
      • (2)管道遍历
      • (3)协程与管道
      • (4)只读只写管道的声明
      • (5)管道阻塞
    • 4.select功能
    • 5.defer+recover
  • 八、网络编程
    • 1.创建客户端
    • 2.创建服务器
    • 3.连接测试
    • 4.发送终端数据
  • 九、反射


一、基础

1.DOS命令

D: //转d盘
go build helloworld.go //编译.exe
go run helloworld.go //编译并执行.exe
// /**/ 注释

2.变量

(1)局部变量

var a int //声明变量
a = 10 //赋值
var b = 20 //自动类型推断
c := "qwq" //省略var
var d,e,f = 10 , "jack" , 3.14 //多变量声明,:=等也可

(2)全局变量

var (n1=1n2=2
) //多个声明

(3)数据类型

基本数据类型(数值型):

int , int8 , int16 , int32 , int64 , 
uint, uint8, uint16 , uint32 , uint64 , byte ,
float32 , float64 //可以用科学计数法如 314E-2

基本数据类型(字符型):无,使用

byte

基本数据类型(布尔型):

bool

基本数据类型(字符串):

string
"qwq" or 'q'//无特殊类型采用
`qwq`//有特殊类型采用
//+号拼接

复杂数据类型:指针、数组、结构体、管道、函数、切片、接口、map

类型转换(显式转换):同C++

(4)指针

&num //地址
var ptr *int = &num //指针定义
*ptr //获取地址对应的值,很像C++

(5)运算符

在这里插入图片描述

(6)自定义数据类型

相当于起别名,但是编译器不会认为起名前后是一种数据类型

type myInt int
type 

3.语句

(1)条件语句

类似C++

// if
if 布尔表达式 1 {/* 在布尔表达式 1 为 true 时执行 */
}
else if 布尔表达式2{
}
else{
}
// switch,注意:自带break
switch var1 {case val1:...case val2:...default:...
}// select

(2)循环语句

// 格式
for init; condition; post {
}
for condition {
}
// range示例
for key, value := range oldMap {newMap[key] = value
}
//无限循环
for true{
}
for ;;{
}
for {
}
break //类似
break label1 //将指定label的循环终止(可以是大循环)
continue //同C++
return 

4.函数

(1)基础

// 格式,函数名首字母大写,可以被本包和其他包文件使用;首字母小写,只能被本包使用
func function_name( [parameter list] ) [return_types] {函数体
}
// 示例,不返回值时,[return_types]是可以被省略的
func swap(x, y string) (string, string) {return y, x
}
func swap(x, y string) (y string, x string) {return
}

注意:go不支持函数重载

// 可变参数,可以传入任意个变量,传入变量当做切片来处理
func test(args...int){
}
// 地址传递,同C++
test(&num)
func test(num int*){*num = 10
}

函数也是一种数据类型,可以赋值给变量,通过该变量可以实现函数调用,函数可以做为形参。

a := test
a(10) //调用函数
// 函数可以作为形参
fun test2(testFunc func(int)){
}

(2)结构

说明:

func (目标形参名 绑定目标) 函数名(传入形参名 形参数据类型, 传入形参名 形参数据类型) ([返回的变量] 返回数据类型){
}
/* 常规函数 */
func 函数名(传入形参名 形参数据类型, 传入形参名 形参数据类型) ([返回的变量] 返回数据类型){
}
func 函数名(传入形参名 形参数据类型, 传入形参名 形参数据类型) {
}
/* 工厂模式 */
func 首字母大写函数名(传入形参名 形参数据类型, 传入形参名 形参数据类型) *结构体名{
}
/* 方法 */
func (目标形参名 绑定目标) 函数名(传入形参名 形参数据类型, 传入形参名 形参数据类型) ([返回的变量] 返回数据类型){
} // 值传递
func (目标形参名 *绑定目标) 函数名(传入形参名 形参数据类型, 传入形参名 形参数据类型) ([返回的变量] 返回数据类型){
} // 指针传递

示例:

// 定义一个后续需要的结构体
type person struct{Name stringage int
}/* 常规函数 */
func GetPerson(Name string, Age int ,Year int) (NewYear int){fmt.Println("正在计算" + Name + "现在的年龄")NewYear = Age + Yearreturn
}
func GetPerson(Name string, Age int ,Year int) (int){fmt.Println("正在计算" + Name + "现在的年龄")NewYear = Age + Yearreturn NewYear
}
func GetPerson(Name string) {fmt.Println("正在计算" + Name + "现在的年龄")
}
/* 工厂模式 */
func NewPerson(name string) *person{return &person{Name : name}
}
/* 方法 */
//与常规函数类似,只是多了一个绑定
func (p person) GetPerson(传入形参名 形参数据类型, 传入形参名 形参数据类型) ([返回的变量] 返回数据类型){
} // 值传递
func (p *person) 函数名(传入形参名 形参数据类型, 传入形参名 形参数据类型) ([返回的变量] 返回数据类型){
} // 指针传递,函数调用和结构体属性调用无需&或*

二、常用函数

0.基础

(1)len() 和 cap()

len同python,cap可以提供切片最长长度

(2)new()

num := new(int) // new的实参是一个类型,返回值是对应类型的指针

(3)make(Type, len, cap)

内存分配的内置函数:数据类型(slice,map,channel),长度(对于slice是必选),提前预留长度(可选)

(4)nil

判断数组时表示空

(5)append() 和 copy()

切片的追加和复制

num2 = append(num, 0) // 追加切片
num = copy(num2, num) // 把num的内容复制到num2,往往用于增加切片容量

(6)Range关键字

用于 for 循环中迭代数组(array)、切片(slice)、通道(channel)或集合(map)的元素。在数组和切片中它返回元素的索引和索引对应的值,在集合中返回 key-value 对。

for key, value := range oldMap {newMap[key] = value
}
for key := range oldMap {newMap[key] = value
}
for key, _ := range oldMap {newMap[key] = value
}

(7)_

忽略作用

(8)element()

value, ok := element.(T)

value为变量的值
ok为bool类型
element是interface变量
T是断言类型

(9).(type)

type关键字,数据类型判断,与switch配合使用:

// 实现数据类型的判断
func checkType(i interface{}) string {switch i.(type) {case string:return "string"case int:return "int"case float64:return "float64"default:return "unknown"}
}

1.package unsafe

(1)unsafe.Sizeof(num)

返回类型尺寸

2.import “fmt”

(1)输出

Printf("it is %T",num) //格式化输出
Println(num) //直接输出,相邻参数间添加空格
Print("num") //直接输出,相邻非字符串间添加空格
str = Sprintf("it is %T",num) //可以保存字符串
var num2 string = fmt.Sprintf("qwq %d",num) //将num转为string

(2)输入

Scanln(&num) //获取输入,必须是地址,类型需要匹配
Scanf("%d %s" , &age , &name) //获取输入,多个输入,输入需要空格相间

在这里插入图片描述

3.import “strconv”

在这里插入图片描述

注意:也可用Sprintf()实现同样功能

在这里插入图片描述
注意:无效值会被转为默认值

4.字符串函数

strconv 和 strings
在这里插入图片描述
rune 类似 int32,以此方式遍历更兼容汉字

在这里插入图片描述
在这里插入图片描述

5.import “os”

在这里插入图片描述

6.import “io”

7.import “io/ioutil”

8.import “bufio”

9.import “sync”

三、包及其函数

1.包的引入

建议包(package)的声明和文件夹名一致,函数首字母必须大写。同一目录下包名必须一致

import "文件夹路径" //import引入
import ( //多个包导入"fmt"test "gocode/testproject01/demo01" //起别名,用于调用
)
包名.函数名 //调用

2.init函数

优先级:全局变量>其他包的init>本包的init>main,用法和main差不多

3.匿名函数与闭包

只被用一次的函数,也可以定义为全局的
在这里插入图片描述
闭包,对内存消耗大:

func getSum() func (int) int {var sum int = 10return func (num int) int{ //匿名函数,该函数引用的变量会一直保存在内存中,可以一直使用sum = sum + numreturn sum}
}
// 闭包:返回的匿名函数+匿名函数以外的变量sum
func main(){f := getSum()fmt.Println(f(1)) //11 传参到匿名函数
}

4.defer关键字

把defer后的语句压入栈中

defer Println("qwq") //函数截止时执行并释放

5.日期和时间函数

import "time"
now := time.Now() //获取当前时间,返回一个结构体

在这里插入图片描述

详情见 https://studygolang.com/pkgdoc

6.错误处理

(1)错误捕获

defer func(){ //捕获错误,使得程序可以继续err := recover()if err != nil{...}
}() //加括号用于调用

(2)自定义错误

import "errors"
err := errors.New("报错...") //报错,并继续运行panic(err) //报错,并终止运行

四、数组、切片、映射

1.数组

数组声明

// 一维数组
var numbers = [5]int{1, 2, 3, 4, 5}
numbers := [5]int{1, 2, 3, 4, 5}
var numbers = [...]int{1, 2, 3, 4, 5}
numbers := [...]int{1:20, 4:99, 3:25}
// 多维数组
var variable_name [SIZE1][SIZE2]...[SIZEN] variable_type //格式
var arr = [2][3]int{{1,4,7},{2,5,8}}
var arr [2][3]int = [2][3]int{{1,4,7},{2,5,8}}

形参数组

func myFunction(param []int) { //未设定数组大小....
}
func myFunction(param [10]int) { //固定数组大小....
}

2.切片 []

var s = []int{1,2,3,4 } //声明方式
s :=[] int {1,2,3 } 
var s[]type = make([]type, len) //采用make的声明方式
s := make([]type, len)arr = s[startIndex:endIndex] // 类似python,包含左不包含右

切片的切片发生变动时,原切片也会发生改变;
参见append() 和 copy()

3.集合/字典/映射 Map

// 使用 make 函数
m := make(map[KeyType]ValueType, initialCapacity)
// 使用字面量创建 Map
m := map[string]int{"apple": 1,"banana": 2,"orange": 3,
}
// 删除键值对
delete(m, "banana")
//查询相关映射是否存在
value, flag := m[orange]

五、面向对象

1.结构体

(1)基础

类似C++,但是golang没有类(class),无重载、构造函数、析构函数、隐藏的this指针等;
但仍有继承、封装、多态等特性

type Books struct {title stringauthor stringsubject stringbook_id int
}variable_name := Books {value1, value2...valuen} //声明变量
或
variable_name := Books { key1: value1, key2: value2..., keyn: valuen}结构体.成员名 //访问方式

对于结构体指针,直接指针名加“.”来调用

struct_pointer.title

在这里插入图片描述
有指针的简化赋值,特别是在“方法”中, & t . 属性 \&t.属性 &t.属性 ∗ t . 属性 *t.属性 t.属性 都可以简写为 t . 属性 t.属性 t.属性

(2)转换

两个结构体的名字、个数和类型必须一致,需要运用强制类型转换。

(3)方法的引入

作用在指定数据类型,不仅是适用于struct的。可以为结构体等设置专用函数

type Person struct{Name string
}
func (p Person) test(){ //方法的绑定fmt.Println(p.Name)
}
func main(){var p Personp.Name = "丽丽"p.test()
}//其他类型的绑定
type integer int
func (i integer) print(){ //方法的绑定fmt.Print1n("i = ",i)
}
func main(){var i integer = 20i.print()
}

注意:

  • test方法中参数名字随意起。
  • 结构体Person和test方法绑定,调用test方法必须靠指定的类型: Person。
  • 如果其他类型变量调用test方法一定会报错。
  • 结构体对象传入test方法中,值传递,和函数参数传递一致。

(4)方法与函数的差别

  • 绑定指定类型:
    方法:需要绑定指定数据类型
    函数:不需要绑定数据类型
  • 调用方式不一样:
    函数的调用方式:
    函数名(实参列表)
    方法的调用方式:
    变量.方法名(实参列表)
  • 对于函数来说,参数类型对应是什么就要传入什么。
  • 对于方法来说,接收者为值类型,可以传入指针类型,接受者为指针类型,可以传入值类型。(都是传值)

(5)创建结构体实例

本包

type Student struct{Name stringAge int
}func main(){var s1 = Student{"Ann", 19} // 按顺序赋值var s2 = Student{ // 按类型赋值Age : 20,Name : "郭哲炜",}// 想要返回结构体的指针类型var s3 = &Student{"Ann", 19}var s4 = &Student{Age : 20,Name : "郭哲炜",}
}

跨包
结构体首字母大写,可以跨包访问;
如果小写,则需要引入工厂模式。

// 在非main包中
import "文件夹路径/model"/* 首字母大写 */
type Student struct{Name stringAge int
}
s = model.Student{"lili",10}/* 首字母小写 */
type student struct{Name stringAge int
}// 工厂模式 
func NewStudent(n string, a int) *student{return &student{n, a}
}

2.封装

封装的引入:
(1)建议将结构体、字段(属性)的首字母小写(大写其实也行)
(2)给结构体所在包提供一个工厂模式的函数,首字母大写(类似构造函数)
(3)提供一个首字母大写的Set方法,用于对属性判断并赋值
(4)提供一个首字母大写的Get方法,用于获取属性的值

封装的实现:

type person struct{Name stringage int // 小写的,别的包不能直接访问
} 
// 工厂模式函数
func NewPerson(name string) *person{return &person{Name : name}		
}
// 定义 Set和 Get "方法"
func (p *person) SetAge(age int){if age > 0 && age < 150{p.age = age} else {fmt.Println("您输入的年龄有误")}
}
func (p *person) GetAge() int{return p.age
}

3.继承

(1)基础

如果一个结构体嵌套了另一个匿名结构体,那么这个结构体可以直接访问匿名结构体的字段和方法。从而实现了继承特性。

type Animal struct{Age intWeight float32
}
// 绑定方法
func (an *Animal) Shout(){fmt.Println("啊啊啊啊啊啊啊啊")
}
func (an *Animal) ShowInfo(){fmt.Println("年龄为: %v,体重为: %v", an.Age, an.Weight)
}
// 定义新结构体
type Cat struct{Animal
}
// 绑定特有方法
func (c *Cat) scratch(){fmt.Println("喵喵喵,挠你")
}func main(){Cat.Animal.Age = 3
}

(2)注意事项

(1)结构体可以使用嵌套的匿名结构体所有字段和方法(不受首字母影响)。
(2)匿名结构体字段访问可以简化。
(3)结构体和匿名结构体有相同的字段或方法时,使用简化版本时,编译器就近原则。

func main(){Cat.Age = 3
}

(4)存在多继承(但不建议使用)
(5)匿名结构体有相同字段时,不能简化,以区分
(6)结构体的匿名字段可以是基本类型
(7)嵌套匿名结构体后,也可以在创建结构体变量(实例)时,直接指定各个匿名结构体字段的值。
(8)嵌入匿名结构体的指针也是可以的。
(9)结构体的字段可以是结构体类型的。(组合模式)

4.接口

(1)基础

(1)接口中定义一组方法,无需实现,不能有变量。
(2)所有方法都必须实现,才算接口。
(3)Golang没有显式的接口关键字。
(4)接口的目的是定义规范。

//接口的定义
type Say interface{sayHello()
}
//接口的实现
type Chinese struct{
}
func (person Chinese) sayHello(){fmt.Println("你好")
}
type American struct{
}
func (person American) sayHello(){fmt.Println("hi")
}
//接口接收函数
func greet(s Say){ // 多态是基于接口实现的s.sayHello()
}func main(){c := Chinese{}a := American{}greet(a) // 函数方式调用接口greet(c)var s Say = a // 指向方法调用接口s.sayHello()var s Say = cs.sayHello()
}

(2)注意事项

(1)接口本身不能创建实例,但可以指向一个实现了该接口的自定义类型的变量
(2)只要是自定义类型变量,就可以实现接口,不仅仅是struct

// 基于上边的代码
type integer int // 自定义的类型
func (i integer) sayHello(){fmt.Println(i)
}
func main(){var i integer = 10var s Say = i // 会输出打印s.sayHello
}

(3)一个自定义类型可以实现多个接口

//与上述代码无关
type CInterface interface{c()
}
type BInterface interface{b()
}
type AInterface interface{CInterfaceBInterfacea()
}
func ...// a(), b(), c() 三个方法都需要被实现

(4)一个接口可以继承多个别的接口
(5)interface类型默认是一个指针(引用类型),默认为nil
(6)空接口没有任何方法:所有类型都实现了空接口,任何一个类型的变量都可以赋值给空接口。

type E interface{
}

(3)多态

多态是基于接口实现的,参考上文(1)中的代码

func greet(s Say){ // 多态是基于接口实现的s.sayHello()
}

(1)多态参数:如之前的例子
(2)多态数组:定义Say数组,可以存放Chinese结构体、American结构体

//接口的定义
type Say interface{sayHello()
}
//接口的实现
type Chinese struct{name string
}
func (person Chinese) sayHello(){fmt.Println("你好")
}
type American struct{name string
}
func (person American) sayHello(){fmt.Println("hi")
}
//接口接收函数
func greet(s Say){ // 多态是基于接口实现的s.sayHello()
}func main(){var arr [3]Sayarr[0] = American{"Ann"}arr[1] = Chinese{"郭哲炜"}arr[2] = American{"Peter"}fmt.Println(arr)
}

6.断言

//接口的定义
type Say interface {sayHello()
}//接口的实现
//中国人
type Chinese struct {name string
}
func (person Chinese) sayHello() {fmt.Println("你好")
}
func (person Chinese) kuaizi() { //专有函数fmt.Println("筷子")
}//美国人
type American struct {name string
}
func (person American) sayHello() {fmt.Println("hi")
}
func (person American) hpop() { //专有函数fmt.Println("Hpop")
}//接口接收函数,断言实现
func greet(s Say) { // 多态是基于接口实现的s.sayHello()/*断言写法1:if判断*/if value, ok := s.(Chinese); ok {value.kuaizi()} else {fmt.Println("美国人不用筷子")}/*断言写法2:switch判断*/switch s.(type) {case Chinese:temp := s.(Chinese)temp.kuaizi()case American:temp := s.(American)temp.hpop()default:fmt.Println("未知的类型")}}func main() {c := Chinese{"郭哲炜"}a := American{"Peter"}greet(a)greet(c)
}

六、文件操作

1.文件

打开和关闭文件:

import ("os""fmt"
)func main(){//打开文件file, err := os.Open("d:/Test.txt")if err != nil{ //出错fmt.Println("文件出错,问题为:",err)}//没有出错,输出文件fmt.Printf("文件=%v",file)/*关闭文件 方法1*/err2 := file.Close();}if err2 != nil{ //出错fmt.Println("关闭失败")}

2.io读取文件

内含有Open和Close的函数的操作:

import ("fmt""io/ioutil"
)func main(){//读取文件:content, err = ioutil.ReadFile("d:/Test.txt") //返回内容为[]byte,errif err != nil{ //出错fmt.Println("读取出错",err)}//成功fmt.Printf("%v",string(content))		
}

不内含有Open和Close的函数的操作(推荐):

import ("bufio""fmt""io""os"
)func main() {//打开文件file, err := os.Open("d:/Test.txt")if err != nil { //出错fmt.Println("文件出错,问题为:", err)}/*关闭文件 方法2*/defer file.Close()//创建一个流reader := bufio.NewReader(file)//读取方法for {str, err := reader.ReadString('\n') //读到换行截止fmt.Println(str)if err == io.EOF {break}}
}

3.io写入文件

函数:

func OpenFile(name string, flag int, perm FileMode) (file *File, err error)

(1)参数一:文件路径
(2)参数二:文件打开模式(用“|”组合)

const (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_CREATE配合使用,文件必须不存在O_SYNC   int = syscall.O_SYNC   // 打开文件用于同步I/OO_TRUNC  int = syscall.O_TRUNC  // 如果可能,打开时清空文件
)

(3)权限控制(linux/unix才生效)

import("fmt""os""bufio"
)
func main(){//打开文件file, err := os.OpenFile(d:/Demo.txt, os.O_RDWR | os.O_APPEND | os.O_CREATE, 0666)if err != nil{fmt.Println("打开文件失败", err)return}//关闭文件defer file.Close()//写入文件:IO流→缓冲输出流(带缓冲区)writer := bufio.NewWriter(file)for i := 0; i < 10; i++{writer.WriteString("你好 郭哲炜\n")}//流带缓冲区,刷新数据→真正地写入文件中:writer.Flush()
}

4.文件复制操作

读取+写入(不推荐使用ioutil):

import("fmt""io/ioutil"
)
func main(){//定义源文件file1 := "d:/Demo.txt"//定义目标文件file2 := "d:/Demo2.txt"//对文件进行读取content, err := ioutil.ReadFile(file1)if err != nil{fmt.Println("读取失败")return}//写出文件err2 = ioutil.WriteFile(file2,content,0666)if err2 != nil{fmt.Println("写出失败")return}

七、协程和管道

1.基本概念

(1)程序

是为了完成特定任务,用某种语言编写的一组指令的集合,是一段静态的代码。

(2)进程

是程序的一次执行过程,进程作为资源分配的单位,在内存中会为每个进程分配不同的内存区域。它是动态的,有自身产生、存在和消亡的过程。

(3)线程

进程可进一步细化为线程,是一个程序内部的一条执行路径。

(4)协程

又称为微线程,是一种用户态的轻量级线程。

2.协程

(1)多协程

协程可以多个,但是主程序结束时,协程也会跟着结束。

func main(){//启动多个协程for i := 1; i <= 5; i++{go func(){fmt.Println("hello world")}()}
}

(2)延长协程存活

延长协程的存活时间 sync.WaitGroup

import ("fmt""sync"
)
var wg sync.WaitGroup //只定义无需赋值
func main(){//启动多个协程for i := 1; i <= 5; i++{wg.Add(1) //协程开始时加一go func(){fmt.Println("hello world")defer wg.Done() //协程结束时减一,可以结合defer使用}()}//主线程一直在阻塞,什么时候wg减为0,则停止wg.Wait()
}

(3)互斥锁同步协程

解决多个协程操作一个数据,结果混乱的情况。但互斥锁效率比较低

import ("fmt""sync"
)
var totalNum int
var wg sync,WaitGroup
//互斥锁
var lock sync.Mutex
func add(){defer wg.Done()for i := 0; i < 1000; i++{//加锁lock.Lock()totalNum = totalNum + 1//解锁lock.Unlock()}
}
func sub(){defer wg.Done()for i := 0; i < 1000; i++{//加锁lock.Lock()totalNum = totalNum - 1//解锁lock.Unlock()}
}
func main(){wg.Add(2) //启动协程go add()go sub()wg.Wait() //阻塞主进程fmt.Println(totalNum)
}

(4)读写锁

写和读之间才会产生影响,单单在读的时候不会彼此产生影响。

import ("fmt""sync""time"
)
var totalNum int
var wg sync,WaitGroup
//互斥锁
var lock sync.RWMutex
func read(){defer wg.Done()lock.RLock() //如果只是读数据,则这个锁不会产生影响,同时读写则会有影响fmt.Println("开始读取数据")time.Sleep(time.Second)	fmt.Println("读取数据完毕")lock.RUnlock()
}
func sub(){defer wg.Done()lock.Lock() //如果只是读数据,则这个锁不会产生影响,同时读写则会有影响fmt.Println("开始写入数据")time.Sleep(time.Second*10)	fmt.Println("写入数据完毕")lock.Unlock()
}
func main(){wg.Add(2) //启动协程for i := 0; i < 5; i++{go read()}	go write()wg.Wait() //阻塞主进程
}

3.管道

本质是数据结构里的队列,先进先出,线程安全无需加锁。
管道是有类型的,一个string的管道只能放string类型。

var intChan chan int //声明定义
intChan = make(chan int, 3) //初始化空间
intChan <- 10 //存放数据
num := <- intChan //取出数据

在没有使用协程的情况下,如果数据已经全部取出,再取会报错(不能放多,不能取多)

(1)管道关闭

可以取,不可放。

close(intChan)

(2)管道遍历

(3)协程与管道

(4)只读只写管道的声明

(5)管道阻塞

4.select功能

5.defer+recover

八、网络编程

1.创建客户端

2.创建服务器

3.连接测试

4.发送终端数据

九、反射

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/bicheng/11063.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

第十四篇:数据库设计精粹:规范化与性能优化的艺术

数据库设计精粹&#xff1a;规范化与性能优化的艺术 1. 引言 1.1 数据库设计在现代应用中的核心地位 在数字化的浪潮中&#xff0c;数据库设计如同建筑师手中的蓝图&#xff0c;是构建信息大厦的基石。它不仅关乎数据的存储与检索&#xff0c;更是现代应用流畅运行的生命线。…

【SpringBoot】解锁后端测试新境界:学习Mockito与MockMvc的单元测试魔法

文章目录 前言&#xff1a;Java常见的单元测试框架一.Junit5基础二.SpringBoot项目单元测试1.添加依赖2.SpringBoot单元测试标准结构3.SpringBoot单元测试常用注解 三.单元测试中如何注入依赖对象1.真实注入&#xff08;AutoWired、 Resource&#xff09;2.Mock注入2.1.前言2.2…

资料总结分享:瀑布图,GESA,生存曲线

目录 瀑布图 GESA 生存曲线 瀑布图 肿瘤的瀑布图是一种常用的数据可视化方式&#xff0c;用于展示个体患者或研究样本的肿瘤基因组学数据&#xff0c;通常包括基因突变、基因拷贝数变异、染色体重排等信息。 颜色编码&#xff1a; 柱状图通常会使用颜色编码来表示不同类型…

Redis的数据淘汰策略——Java全栈知识(19)

Redis的数据淘汰策略 什么是数据淘汰策略 数据过期策略是 redis 中设置了 TTL 的数据过期的时候 Redis 的处理策略。数据淘汰策略是 Redis 内存不够的时候&#xff0c; 数据的淘汰策略&#xff1a;当 Redis 中的内存不够用时&#xff0c;此时在向 Redis 中添加新的 key, 那么…

IEEE 802.11标准

在IEEE 802.11标准中使用了扩频通信技术&#xff0c;主要作用是使得抗干扰性更强。 IEEE 802.11在MAC层采用了CSMA/CA协议。 IEEE 802.1x是一种基于端口认证协议。

开源web在线数据库设计软件 —— 筑梦之路

GitHub - drawdb-io/drawdb: Free, simple, and intuitive online database design tool and SQL generator. 简介 DrawDB是一款多功能且用户友好的在线工具&#xff0c;允许用户轻松设计数据库实体关系。通过简单直观的界面&#xff0c;DrawDB使用户能够创建图表、导出SQL脚本…

kettle经验篇:MongoDB-delete插件问题

目录 项目场景 问题分析 解决方案 MongoDB Delete插件使用总结 项目场景 项目使用的ODS层数据库是MongoDB&#xff1b;在数据中心从DB层向ODS层同步数据过程中&#xff0c;发现有张ODS表在同步过程中&#xff0c;数据突然发生锐减&#xff0c;甚至于该ODS表数据清0。 同步…

算法设计与分析 例题 绘制Huffman树、循环赛、分治、最短路与动态规划

1.考虑用哈夫曼算法来找字符a,b,c,d,e,f 的最优编码。这些字符出现在文件中 的频数之比为 20:10:6:4:44:16。要求&#xff1a; &#xff08;1&#xff09;&#xff08;4 分&#xff09;简述使用哈夫曼算法构造最优编码的基本步骤&#xff1b; &#xff08;2&#xff09;&…

Oracle 流stream数据的复制

Oracle 流stream数据的复制 --实验的目的是捕获scott.emp1表的变化&#xff0c;将变化应用到远程数据库scott.emp1表中。 --设置初始化参数 AQ_TM_PROCESSES1 COMPATIBLE9.2.0 LOG_PARALLELISM1 GLOBAL_NAMEStrue JOB_QUEUE_PROCESSES2 --查看数据库的名称&#xff0c;我的为o…

2024中国(重庆)机器人展览会8月举办

2024中国(重庆)机器人展览会8月举办 邀请函 主办单位&#xff1a; 中国航空学会 重庆市南岸区人民政府 招商执行单位&#xff1a; 重庆港华展览有限公司 2024中国重庆机器人展会将汇聚机器人全产业链知名企业&#xff0c;世界科技领先的生产制造企业与来自多个国家和地区…

蓝桥杯-网络安全比赛(6) 模拟实验 Metasploit 控制并获取Windows 登录HASH、LM Hash和NTLM Hash密文解析

窃取WINDOWS账号密码 系统环境&#xff1a;主机&#xff08;Windows系统 IP&#xff1a;192.168.126.129)&#xff0c;虚拟机&#xff08;KALI系统 IP&#xff1a;192.168.126.3&#xff09;&#xff0c;两者需要能通过本地网络互通互连。 攻击工具&#xff1a;Metasploit是一…

改变浏览器大小,图片(img)内容居中显示img标签,不是背景图

改变浏览器大小,图片&#xff08;img&#xff09;内容居中显示&#xff0c;img标签&#xff0c;不是背景图 效果直接上图&#xff1a; 上代码&#xff1a; <!DOCTYPE html> <html> <head><title>测试图片居中显示&#xff0c;高度不变只变宽度<…

Electron学习笔记(五)

文章目录 相关笔记笔记说明 七、系统1、系统对话框2、自定义窗口菜单3、系统右键菜单4、快捷键(1)、监听网页按键事件 &#xff08;窗口需处于激活状态&#xff09;(2)、监听全局按键事件 &#xff08;窗口无需处于激活状态&#xff09;(3)、补充&#xff1a;自定义窗口菜单快捷…

异常处理/ROS2异常处理模块源码解读与浅析

文章目录 概述ros2/rcutils/src/error_handling模块自身异常处理错误状态结构与存储本模块初始化错误状态的设置错误状态的获取错误状态的清理不丢失旧错误状态把手段还原为目的其他 概述 本文从如下几个方面对 ROS2.0 中 rcutils 库 error_handling 错误处理模块的源码进行解…

花了24小时做的采购、库存、进销存excel模板,真心好用,免费分享

花了24小时做的采购、库存、进销存excel模板&#xff0c;真心好用 在企业的日常运营中&#xff0c;进销存管理是一项至关重要的任务。它不仅涉及到商品的采购、销售和库存管理&#xff0c;还直接影响到企业的财务状况和市场竞争力。为了提高管理效率&#xff0c;许多企业选择使…

Redis 的 SDS 和 C 中字符串相比有什么优势?

C 语言使用了一个长度为 N1 的字符数组来表示长度为 N 的字符串&#xff0c;并且字符数组最后一个元素总是 \0&#xff0c;这种简单的字符串表示方式 不符合 Redis 对字符串在安全性、效率以及功能方面的要求。 C语言的字符串可能有什么问题&#xff1f; 这样简单的数据结构可…

别人家的UI表单为什么这么漂亮?而你却千篇一律。

设计漂亮的移动UI页面表单页需要考虑以下几个方面&#xff1a; 布局和结构设计 合适的布局和结构&#xff0c;使表单页面看起来整洁、清晰&#xff0c;并且易于使用。可以使用网格系统或者栅格布局来对表单进行划分&#xff0c;使不同的表单元素有明确的位置和排列。 色彩和配…

GO+树莓派+E53_IA1智慧农业模块

简介 之前手头上有小熊派的开发板&#xff0c; 有一个E53_IA1模块&#xff0c; 刚好用到树莓派上&#xff0c; 使用GO进行控制&#xff0c;实现智慧农业模块功能。 模块介绍 模块电路介绍 按硬件分成五块&#xff0c; 其中四块在本次用上了&#xff0c; 分别是 1. 补光模块&…

可视化大屏:城市治理方向,三维地图那是相当震撼呀。

随着城市化进程的加快&#xff0c;城市治理变得越来越复杂&#xff0c;需要大量的数据和信息来支持决策和管理。在这个背景下&#xff0c;可视化大屏作为一种新兴的信息展示工具&#xff0c;正逐渐在城市治理中发挥着重要作用。 首先&#xff0c;可视化大屏能够将庞大的数据和信…

kettle从入门到精通 第五十九课 ETL之kettle 邮件发送多个附件,使用正则轻松解决

想真正学习或者提升自己的ETL领域知识的朋友欢迎进群&#xff0c;一起学习&#xff0c;共同进步。若二维码失效&#xff0c;公众号后台加我微信入群&#xff0c;备注kettle。 问题场景&#xff1a; 一个朋友说他用kettle将生成好的多个文件&#xff08;a.xls和b.xls&#xff0…