Let's GO(四)

摘要:
控制共享资源只能通过一个goroutine import“sync”var(xintwg.sync.WaitGrouplocksync.Mutex)funcadd(){fori:

人生苦短,Let's GO

今天我学了什么?

1.panic && recover

Go的logo是一只萌萌的囊地鼠(Gopher)
当Go程序出现错误,程序将报panic(恐慌)
所以是错误代码吓到小地鼠了吗哈哈
然后需要用recover来安抚它
类似于 try-catch的语法

func b() {
	defer func() { //defer must declare before panic
		err:=recover()  //defer + recover(),recover panic
		if err !=nil {
			fmt.Println("func b err")
		}
	}()
	panic("panic in func b") //panic 触发结束程序
}

2.goroutine(协程)

非常方便的并发操作,
一个goroutine对应一个函数

func hello() {
	fmt.Println("Hello World!")
	wg.Done() //goroutine done,count-1
}

//替代time.Sleep(),确保goroutine都执行完毕
var wg sync.WaitGroup  

func main() {
	wg.Add(1)  //count+1
	for i := 0; i < 1000; i++ {
		wg.Add(1)
		go func(i int) {
			fmt.Println("hello",i) //形成了闭包,i公用
			wg.Done()
		}(i)
	}  //多线程执行数不同
	go hello()  //open a goroutine
	fmt.Println("hello world")
	//time.Sleep(time.Second)
	wg.Wait() //all goroutine done
}

3.锁

因为不同goroutine可能会同时读写同一个资源,所以需要给资源加锁

a. 互斥锁(sync.Mutex):控制共享资源只被一个goroutine获取

import "sync"

var (
	x int
	wg. sync.WaitGroup
	lock sync.Mutex
)

func add()  {
	for i := 0; i < 5000; i++ {
		lock.Lock()   //将x值加锁
		x ++
		lock.Unlock() //执行完将x的锁取消
	}
	wg.Done()
}

b.读写互斥锁:适用于读多写少的情况,速度更快。

var (
	xx  int64
	rwlock sync.RWMutex //读写互斥锁
)

func read()  {
	rwlock.RLock()  //加读锁
	time.Sleep(time.Millisecond)
	rwlock.RUnlock()
	wg.Done()
}

func write() {
	rwlock.Lock() //写加读写锁
	xx = xx + 1
	time.Sleep(time.Millisecond*10)
	rwlock.Unlock()
	wg.Done()
}

4.channel(通道)

Go语言的并发模型是CSP(Communicating Sequential Processes),提倡通过通信共享内存而不是通过共享内存而实现通信。
一个goroutine的值通过channel发送给另一个goroutine
通道FIFO(First in,First out)

//ch1<-
func f1(ch chan<- int)  { //(单向通道)chan<- 表示只能发送到通道
	for i := 0; i < 100; i++ {
		ch <- i
	}
	close(ch)
}

//( ch2 <- ch1 )^2
func f2(ch1 <-chan int,ch2 chan<- int)  { //<-chan 表示只能从通道读取
	for  {
		tmp,ok := <- ch1
		if !ok {
			break
		}
		ch2 <- tmp*tmp
	}
	close(ch2)
}

func main() {
	ch1 := make(chan int,100)  //初始化通道,100为缓冲区大小
	ch2 := make(chan int,200)

	go f1(ch1)
	go f2(ch1,ch2)

	for val := range ch2 {
		fmt.Println(val)
	}
}

5.网络编程(tcp,udp)

server:1.监听端口 2.建立与client的链接 3.与之交互
client:1.建立与server的链接 2.发送信息 3.关闭连接

//tcp_server_demo
func process(conn net.Conn)  {
	defer conn.Close() //close connection
	//data
	for  {
		reader := bufio.NewReader(conn) //read message
		var buf [128]byte
		n,err := reader.Read(buf[:])
		if err != nil {
			fmt.Println("read err,err:",err)
			break
		}
		recv := string(buf[:n])
		fmt.Printf("get:%v
",recv) //show message
		conn.Write([]byte("ok")) //reply
	}
}


func main() {
	//listen
	listen,err := net.Listen("tcp","127.0.0.1:20000")
	if err != nil {
		fmt.Printf("listen port failed,err:%v
",err)
		return
	}
	//waiting for connection
	for {
		conn,err := listen.Accept()
		if err != nil {
			fmt.Printf("accept failed.err:%v
",err)
			continue
		}
		//go!
		go process(conn)
	}
}
//tcp_client_demo
func main() {
	//conn
	conn,err := net.Dial("tcp","127.0.0.1:20000")
	if err != nil {
		fmt.Println("dial failed,err:",err)
		return
	}
	//send and receiver
	input := bufio.NewReader(os.Stdin)
	for  {
		s,_ := input.ReadString('
')
		s = strings.TrimSpace(s)
		if strings.ToUpper(s) == "q" { //q for quit
			return
		}
		//send message
		_,err := conn.Write([]byte(s))
		if err !=nil {
			fmt.Printf("send failed,err:%v
",err)
			return
		}
		//receiver
		var buf [1024]byte
		n,err := conn.Read(buf[:])
		if err != nil {
			fmt.Printf("read failed,err:%v
",err)
			return
		}
		fmt.Println("get reply: ",string(buf[:n]))
	}
}

客户端发送,服务端接收并发送反馈信息
在这里插入图片描述

这里要先运行server端,不然client端找不到端口会恐慌的
在这里插入图片描述

6. 单元测试

执行go test来测试功能是否符合预期

func TestSplit(t *testing.T) {  //测试哪个函数就叫Testxxx()
	type test struct {
		input string
		sep string
		want []string
	}
	tests := map[string]test {  //给出几组样例,OJ或者leetcode不会就是这样的吧..
		"simple":test {"ab c"," ",[]string{"ab","c"}},   //给测试数据命令,可以针对这组数据进行测试 
		"multi sep":test{"hello,World",",",[]string{"hello","World"}},
	}
	for name,tc := range tests {
		t.Run(name, func(t *testing.T) {
			got := Split(tc.input,tc.sep)
			if !reflect.DeepEqual(got,tc.want) {
				t.Errorf("name:%s want:%v got:%v
",name,tc.want,got)
			}
		})
	}
}

通过测试:
在这里插入图片描述
未能通过测试:
在这里插入图片描述

性能测试,将跑够足够的量来测试

//性能基准测试
func BenchmarkSplit(b *testing.B) {
	//b.N 由go test决定是否继续加大测试量
	for i := 0; i < b.N; i++ {
		Split("a,v,c",",")
	}
}

将给出详细的测试结果:
在这里插入图片描述

总结

Go的基础语法到这里就粗略的过一遍了,如果要打磨自己的基础的,可以从学校OJ一直到acm题多加练习。代码量上去了,理解也会水涨船高。

我可能没有将Go基础语法全部内容写出来,而且我可以说只是复制粘贴稍微整理了一下代码,
更加详细的教学请移步大佬的博客李文周的博客
再次感谢前辈的教学内容。

挺看好Go的前景的,能从Go的各方各面感觉到这个语言的蓬勃生气,
但可能因为还是初学者,眼界不够,暂时也说不出个子丑寅卯来,继续学吧。


人生苦短,GO!GO!GO!

免责声明:文章转载自《Let's GO(四)》仅用于学习参考。如对内容有疑问,请及时联系本站处理。

上篇Vue全家桶系列【vue3更新中.....】将定时任务cron 解析成中文下篇

宿迁高防,2C2G15M,22元/月;香港BGP,2C5G5M,25元/月 雨云优惠码:MjYwNzM=

相关文章

iOS进阶之多线程

多线程 注意:iOS关于UI的刷新和添加必须在主线程中操作! pthread的创建方法: pthread_t pthread; //第一个参数 线程指针 //第二个参数 线程的一些属性 //第三个参数 函数指针 用于执行方法 //第四个参数 线程中的传值 pthread_create(&pt...

MongoDB聚合

--------------------MongoDB聚合-------------------- 1、aggregate():     1、概念:         1、简介             MongoDB中聚合(aggregate)主要用于处理数据(诸如统计平均值,求和等),并返回计算后的数据结果。有点类似sql语句中的 count(*), s...

kubernetes之健康状态检测

1.说明容器探针: kubelet 对容器执行的定期诊断探针执行方式: LivenessProbe: 判断容器是否存活 running状态, 如果不健康kubelet就会杀掉pod,根据重启策略RestartPolicy进行相应的处理 ReadinessProbe: 判断容器是否处于可用Ready状态, 达到ready状态表示pod可以接受请求, 如果...

ansible-任务控制tags

1. ansible-任务控制tags介绍        如果你有一个大型的剧本,那么只能运行它的特定部分而不是在剧本中运行所有内容可能会很有用。因此,Ansible支持“tags:”属性。        执行playbook时,可以通过两种方式根据 “tags” 过滤任务 在命令行上,使用或选项“--tags或 --skip-tags ” 在ansi...

loki 数据库详解

介绍 LokiJS是一个面向文档的javascript数据库,与MongoDB有点相似。 它支持索引,查询和过滤数据集合。 LokiJS还支持更高级的功能,例如mapReduce,事务,并允许您实现自定义远程同步以将数据保存到服务器(或移动设备上的本地文件)。 磁盘的持久性已经在诸如nodejs之类的CommonJS环境中实现, 在移动设备上,您只需要请求...

Linux服务器更换主板后,网卡识别失败的处理方法

1)现象说明公司IDC机房里的一台线上服务器硬件报警,最后排查发现服务器主板坏了,随即联系厂商进行更换主板,最后更换后,登录服务器,发现网卡绑定及ip信息都在,但是ip却ping不通了,进一步排查,重启网卡,提示之前的eth0和eth1网卡设备发现不了了,也就是说服务器主板更换后,之前的网卡设备都识别不了了! 主板更换后,重启并登陆服务器,发现之前的网卡设...