golang 基础语法的使用


golang数组

var a [3]int  //定义一个大小为3的元组
var a [3]int{1, 2, 3}  //设定初始值
a := []int{1, 2, 3}  // 一般的写法,可以设定初始值,让go 自己判定元素多少。
a[1] = 9  //更改值
a = append(a, 10)  //末尾添加值

golang映射(词典)

numbers := make(map[string]int) //关键词的数据类型和对应的数据类型。
numbers["one"] = 1
numbers["two"] = 2
delete(numbers, "one") // 通过delete关键词删除一个映射
fmt.Println(numbers)

map[two:2]

golang for循环

func main() {
	for i := 0; i < 5; i++ { //三部分,第一个部分是整个循环开始之前需要执行的代码,第二部分是程序执行的判断条件,决定是否执行下去,第三部分是循环结束后执行的代码。
		fmt.Println(i)
	} // for循环中的任意一个循环都可以被忽略。
}

0
1
2
3
4

golang 利用for遍历数组

func main() {
	a := []int{1, 2, 3, 4, 5}
	for k, v := range a {
		fmt.Println(k, v)
	}
}

0 1
1 2
2 3
3 4
4 5

golang 利用for遍历词典

func main() {
	numbers := make(map[string]int) //关键词的数据类型和对应的数据类型。
	numbers["one"] = 1
	numbers["two"] = 2
	for k, v := range numbers {
		fmt.Println(k, v)
	}
}

one 1
two 2

go 函数

# 例1
func main() {
	fmt.Println(add(1, 3))
}
func add(x int, y int) int {
	return x + y
}

# 例2
func main() {
	// fmt.Println(add(1, 3))
	sum, product := do_math(2, 3)
	fmt.Println(sum, product)
}

func do_math(x int, y int) (int, int) {  //可以接收两个值,返回两个值
	return x + y, x * y
}

golang 闭包

pass

golang 指针

func main() {
	n := 0
	add(&n) // 传入的是这个参数的地址
	fmt.Println(n)
}
func add(n *int) { //这个'*'表示接收一个指向这个整数的指针,也就是地址
	*n = *n + 1 //通过这个地址的值进行更改数据
}

1

golang 自定义数据类型

type cat struct { //定义自己的数据类型
	name string
	age  int
}
func main() {
	newCat := cat{name: "Kitty", age: 3}
	newCat.age = 4 //重新定义
	fmt.Println(newCat)
}

{Kitty 4}

golang 给自己的类型定义方法

type MyFloat float64  //定义自己的方法类型

func (n *MyFloat) add() {  //也可以运用指针传参
	*n = *n + 0.5
}

func (n MyFloat) show() {
	fmt.Println(n)
}
func show(n MyFloat) {
	fmt.Println(n)
}

func main() {
	var a MyFloat = 1.5
	a.add()
	a.show()  // 方法类型传参
	show(a)
}

golang 实例

普通的函数功能,问题是如何同时进行呢?

func count(n int, animal string) {
	for i := 0; i < n; i++ {
		fmt.Println(i+1, animal)
		time.Sleep(time.Millisecond * 500) // 睡眠5秒
	}
}
func main() {
	count(5, "羊")
	count(5, "牛")  //这里是按照顺序执行的。
}

goroutine,问题是不知道还有多少goroutine没有执行完成

func count(n int, animal string) {
	for i := 0; i < n; i++ {
		fmt.Println(i+1, animal)
		time.Sleep(time.Millisecond * 500) // 睡眠5秒
	}
}
func main() {
	go count(5, "🚗") //可以创建一个goroutine,其实本质就是协程。
	go count(5, "🛹") //当这里创建两个goroutine的时候,main函数会直接执行完毕,期间创建的所有的goroutine都会被销毁。
}

waitGroup,问题是两个程序是各干各的

func count(n int, animal string) {
	for i := 0; i < n; i++ {
		fmt.Println(i+1, animal)
		time.Sleep(time.Millisecond * 500) // 睡眠5秒
	}
}
func main() {
	var wg sync.WaitGroup
	wg.Add(2) //这个waitgroup其实就是一个计数器
	go func() {
		count(5, "🚗")
		wg.Done() //如果这个协程完成了计数器就减一
	}()
	go func() {
		count(3, "🛹")
		wg.Done()
	}()
	wg.Wait() //等计数器的值等于0的时候就继续执行,也就是完成计算
}

golang channel 信道

如果需要进行交流,可以加一个全局变量,但是问题是每一个goroutine可能是不同cpu的,可能存在同时更改同一块内存的情况。

通常其他的语言比如python就是全局锁,同一时间,只能是一个线程操作一块内存地址。

但是golang 做法是不同的,它的设计者采用了communicating Sequential Processes的一种方法,(我们不通过共享内存来交流,我们要通过交流来共享内存)

注意的是:往channel里发送一条消息,和从channel里面收听一条消息,都会阻塞代码的运行。当我要发送一条消息的时候,我会一直在这里等着,直到这条消息在channel的另一边被接收了。相反如果我想从channel里面接收一条消息,如果有人发送消息,我就会接收到,但是如果没有人发送消息,我就会在这里一直等着。就可以利用这种阻塞代码的特性,来同步我们的代码。

func count(n int, animal string, c chan string) { // 让count函数多接收一个参数channel
	for i := 0; i < n; i++ {
		c <- animal
		time.Sleep(time.Millisecond * 500) // 睡眠5秒
	}
}
func main() {
	channel := make(chan string)
	go count(5, "🚗", channel)
	for { // 因为只能接收一条消息,所以写一个for循环让他不停的接收消息
		message := <-channel
		fmt.Println(message)
	}
}

🚗
🚗
🚗
🚗
🚗
fatal error: all goroutines are asleep - deadlock!

goroutine 1 [chan receive]:
main.main()
        /home/theing/study/go_study/hello/test.go:18 +0xba
exit status 2

上述出现了错误,因为count函数不会有新的值传进去,但是main函数里面一个channel一直等着那边的消息。所以给出了报错。方法就是关闭channel。

通过 channel传过来是否可以通讯的布尔值是否关闭channel

func count(n int, animal string, c chan string) { // 让count函数多接收一个参数channel
	for i := 0; i < n; i++ {
		c <- animal
		time.Sleep(time.Millisecond * 500) // 睡眠5秒
	}
	close(c) //for循环完毕了就关闭这个channel
}
func main() {
	channel := make(chan string)
	go count(5, "🚗", channel)
	for { // 因为只能接收一条消息,所以写一个for循环让他不停的接收消息
		message, open := <-channel //每个channel都有一个布尔值,判断这个channel是否还可以通讯。
		if !open {                 //通过这个布尔值判断是否接收消息。
			break
		}
		fmt.Println(message)
	}
}

通过for循环判断channel

func count(n int, animal string, c chan string) { // 让count函数多接收一个参数channel
	for i := 0; i < n; i++ {
		c <- animal
		time.Sleep(time.Millisecond * 500) // 睡眠5秒
	}
	close(c) //for循环完毕了就关闭这个channel
}
func main() {
	channel := make(chan string)
	go count(5, "🚗", channel)
	for message := range channel { //也可以用for循环进行判断,如果channel关闭了for循环就退出了。
		fmt.Println(message)
	}
}

通过channel交流

问题是阻塞代码

func main() {
	c1 := make(chan string) //创建两个channel
	c2 := make(chan string)
	go func() {
		for {
			c1 <- "🚗"
			time.Sleep(time.Millisecond * 500) //每次发送数据间隔半秒
		}
	}()
	go func() {
		for {
			c2 <- "🚍"
			time.Sleep(time.Millisecond * 2000) //每次发送数据间隔2秒
		}
	}()

	for {
		fmt.Println(<-c1) //发送数据和接收数据都会阻塞代码的运行
		fmt.Println(<-c2)
	}
}

select 语句

func main() {
	c1 := make(chan string) //创建两个channel
	c2 := make(chan string)
	go func() {
		for {
			c1 <- "🚗"
			time.Sleep(time.Millisecond * 500) //每次发送数据间隔半秒
		}
	}()
	go func() {
		for {
			c2 <- "🚍"
			time.Sleep(time.Millisecond * 2000) //每次发送数据间隔2秒
		}
	}()
	for {
		select { //运用select 看那个channel不是阻塞的就运行那个。
		case msg := <-c1:
			fmt.Println(msg)
		case msg := <-c2:
			fmt.Println(msg)
		}
	}
}

实践,对树形结构的数据进行遍历,搜索文件

pass

buffer带有缓存的channel

contast包裹,可以很好的管理goroutine


文章作者: theing
版权声明: 本博客所有文章除特別声明外,均采用 CC BY 4.0 许可协议。转载请注明来源 theing !
评论
  目录