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

文章目录


一、基础

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=1
	n2=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 string
	age int
}

/* 常规函数 */
func GetPerson(Name string, Age int ,Year int) (NewYear int){
	fmt.Println("正在计算" + Name + "现在的年龄")
	NewYear = Age + Year
	return
}
func GetPerson(Name string, Age int ,Year int) (int){
	fmt.Println("正在计算" + Name + "现在的年龄")
	NewYear = Age + Year
	return 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 = 10
	return func (num int) int{ //匿名函数,该函数引用的变量会一直保存在内存中,可以一直使用
		sum = sum + num
		return 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 string
   author string
   subject string
   book_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 Person
	p.Name = "丽丽"
	p.test()
}

//其他类型的绑定
type integer int
func (i integer) print(){ //方法的绑定
	fmt.Print1n("i = ",i)
}
func main(){
	var i integer = 20
	i.print()
}

注意:

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

(4)方法与函数的差别

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

(5)创建结构体实例

本包

type Student struct{
	Name string
	Age 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 string
	Age int
}
s = model.Student{"lili",10}

	/* 首字母小写 */
type student struct{
	Name string
	Age int
}
		// 工厂模式 
func NewStudent(n string, a int) *student{
	return &student{n, a}
}

2.封装

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

封装的实现:

type person struct{
	Name string
	age 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 int
	Weight 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 = c
	s.sayHello()
}

(2)注意事项

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

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

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

//与上述代码无关
type CInterface interface{
	c()
}
type BInterface interface{
	b()
}
type AInterface interface{
	CInterface
	BInterface
	a()
}
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]Say
	arr[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,err
		if 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/O
    O_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.发送终端数据

九、反射

  • 22
    点赞
  • 18
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值