首页 > 其他分享 >go学习

go学习

时间:2023-02-17 20:22:05浏览次数:30  
标签:err int fmt 学习 Println func go conn

go环境变量

$GOPATH

在1.11之前它是我们书写项目的工作目录
在1.11之后它内部存储的是我们公共go.mod下载的依赖包

$GOROOT

安装目录

$GOARCH

目标机器的处理器架构,它的值可以是386、amd64 或arm.

$GoOS

表示目标机器的操作系统,它的值可以是darwin、freebsd、 linux或 windows.

$GOARM

专门针对基于arm架构的处理器,它的值可以是5或6,默认为6。

$GOMAXPROCS

用于设置应用程序可使用的处理器个数与核数

$GOPROXY

配置镜像仓库,下载东西

GO111MODULE

on off auto,包

go并发

有无缓存区

c := make(chan int)   # 没有缓存区
c := make(chan int, 5)  #有5个缓存区
  • 缓存区在存入的时候是会优先把缓存区填满
c := make(chan int, 5)
go func() {
   for i := 0; i < 10; i++ {
      c <- i    
      # 2、添加5个值,填满
      # 4、空出一个缓存区,存入一个
      # 6、又有空出的缓存区 ...
   }
}()
for i := 0; i < 10; i++ {
   fmt.Println(<-c)   
   # 1、先运行到这里,等待c中的值,此时c中没有值 
   # 3、c中有值了,取出第一个0,取出后还有四个
   # 5、又满了,取出1,
}

close

当你不需要存入数据的时候使用这个函数关闭

c <- 1
c <- 2
c <- 3
close(c)
c <- 4  # 已经关闭这个=c继续存值会报错
c <- 5

特殊用法

使用for range语法读取channel中的值的时候必须使用close关闭,否则程序报错

c := make(chan int, 5)
c <- 1
c <- 2
c <- 3
c <- 4
c <- 5
close(c)  #必须使用close关闭
for v := range c {
   fmt.Println(v)
}

select用法

在select中的 case后面的只要是可以执行就会执行,有错误就不会执行‘:‘后面的语句

c1 := make(chan int, 1)
c2 := make(chan int, 1)
c3 := make(chan int, 1)

select {
case <-c1:  #这个是有错误的因为c1中没有存值,不是在这里的话会死锁
   println("c1")
case <-c2:
   println("c2")
case <-c3:
   println("c3")
default:
   println("都不满足!")     # 输出这个
}

后面有多个没有错误就随机执行,即每次执行结果不一样,其实是谁先完成谁就先执行

c1 := make(chan int, 1)
c2 := make(chan int, 1)
c3 := make(chan int, 1)
c1<-1
c2<-1
c3<-1
select {
case <-c1:
   println("c1")
case <-c2:
   println("c2")
case <-c3:
   println("c3")
default:
   println("都不满足!")
}

sync包

Mutex和RWMutex

l := &sync.RWMutex{}
go lockFunc(l)
go lockFunc(l)
go readLockFunc(l)
go readLockFunc(l)
go readLockFunc(l)
go readLockFunc(l)
for{}
func lockFunc(lock *sync.RWMutex) {
   lock.Lock()  // 这个写锁,会排斥其他读锁和写锁,就是在修改这个值的时候其他的协程不能进行读写操作
   fmt.Println("执行一次")
   time.Sleep(1 * time.Second)
   lock.Unlock()
}

func readLockFunc(lock *sync.RWMutex) {
   lock.RLock() //读锁不会排斥其他的读锁,但是排斥写锁,就是在读的时候其他的协程也可以读取,但是不能再写(修改值)了
   fmt.Println("读取一次")
   time.Sleep(1 * time.Second)
   lock.RUnlock()
}

image-20230216133743162

Once

只会执行第一个进来的方法,后面的全部锁死

o := &sync.Once{}
for i := 0; i < 10; i++ {
   o.Do(func() {
      fmt.Println(i)
   })
}

输出: 0

永远只会有这一个结果

查看源码:

func (o *Once) Do(f func()) {
   if atomic.LoadUint32(&o.done) == 0 {
      // Outlined slow-path to allow inlining of the fast-path.
      o.doSlow(f)
   }
}

func (o *Once) doSlow(f func()) {
   o.m.Lock()
   defer o.m.Unlock()
   if o.done == 0 {
      defer atomic.StoreUint32(&o.done, 1)
      f()
   }
}

WaitGroup

类似于原子操作,等Done完之后就会解锁

l := &sync.WaitGroup{}
l.Add(2)  // 设置原始值
go func() {
   time.Sleep(2 * time.Second)
   l.Done()  // 原始值减一,
   //l.Add(-2) 也可以这样直接减二,这时初始值为0,这个值不能为负数,所以使用done方法安全些
   fmt.Println("执行一下")
}()
go func() {
   time.Sleep(3 * time.Second)
   l.Done() // 原始值减一
   fmt.Println("执行一下")
}()
l.Wait()  // 加锁,当原始值为0的时候解锁
fmt.Println("执行完成")

Map

在进行并发的对同一个map进行读写操作的时候,会报错,使用

m := make(map[int]int)
go func() {
   for  {
      m[1] = 1
   }
}()
go func() {
   for  {
      fmt.Println(m[1])
   }
}()
for  {
   
}


输出:
fatal error: concurrent map read and map write

使用sync包的map会自动对这个map进行加锁,所以这个map是线程安全的

m := &sync.Map{}
go func() {
   for  {
      m.Store(1,1)
   }
}()
go func() {
   for  {
      fmt.Println(m.Load(1))
   }
}()
time.Sleep(100)

输出:

...
1 true
1 true
1 true
1 true
1 true
1 true
...

Range

m := &sync.Map{}

m.Store(1,1)
m.Store(2,2)
m.Store(3,3)

m.Range(func(key, value any) bool {
   // 这个Range接收一个func,参数就是key和value
   fmt.Println(key,value)
   time.Sleep(1000)  
   return true  // 这里返回是true继续遍历,否则跳出
})

输出:
1 1
2 2
3 3

Pool

是一个共享的池子,所有人都可以存东西,取的时候随机

p := &sync.Pool{}
p.Put(1)
p.Put(2)
p.Put(3)
p.Put(4)
p.Put(5)
for i := 0; i < 6; i++ {
   fmt.Println(p.Get())
}

输出:
1
5
4
3
2
<nil>

IO

io的几个接口

  • Read(p []byte) (n int, err error)

p:把读取到的数据存在p字节组中,n:读取的字节码数量

  • Write(p []byte) (n int, err error)
  • Close
  • Seek(offset int64, whence int)

这个函数是光标位置,就是对这个给文件操作的位置

offset:操作数,whence:从哪里开始

const (
	SeekStart   = 0 // seek relative to the origin of the file
	SeekCurrent = 1 // seek relative to the current offset
	SeekEnd     = 2 // seek relative to the end
)

其他的对文件的操作的包都会继承并实现这个接口

一个简易的dome:

f,err := os.OpenFile("./read.txt",os.O_CREATE|os.O_RDWR,0777) // os.OpenFile,打开文件,(路径,模式,权限)
/*
	O_RDONLY int = syscall.O_RDONLY // open the file read-only.
	O_WRONLY int = syscall.O_WRONLY // open the file write-only.
	O_RDWR   int = syscall.O_RDWR   // open the file read-write.
	// The remaining values may be or'ed in to control behavior.
	O_APPEND int = syscall.O_APPEND // append data to the file when writing.
	O_CREATE int = syscall.O_CREAT  // create a new file if none exists.
	O_EXCL   int = syscall.O_EXCL   // used with O_CREATE, file must not exist.
	O_SYNC   int = syscall.O_SYNC   // open for synchronous I/O.
	O_TRUNC  int = syscall.O_TRUNC  // truncate regular writable file when opened.
*/
if err != nil {
   fmt.Println(err)
   return
}
defer f.Close()
write:= bufio.NewWriter(f) //定义一个写入缓存区
read := bufio.NewReader(f) //定义一个读取

n := 0
for {
   n++
   str,_, err := read.ReadLine()  //整行读取
   if err != nil {
      break
   }
   write.WriteString(strconv.Itoa(n)+" "+string(str)+"\n") // 写入缓存区
}
f.Seek(0,io.SeekStart) //定义光标位置,这里胸开始的位置,偏移为0
write.Flush() // 从缓存区直接写入到文件中

web基础

demo

server:

package main

import (
   "fmt"
   "net"
)

func main() {
   tcpAddr ,_:= net.ResolveTCPAddr("tcp",":9999")
   listen, _ := net.ListenTCP("tcp", tcpAddr)
   for {
      conn, err := listen.AcceptTCP()
      if err != nil {
         fmt.Println(err)
         break
      }
      go handlerConnection(conn)
   }
}
func handlerConnection(conn *net.TCPConn) {
   for  {
      b := make([]byte, 1024)
      n, _ := conn.Read(b)
      fmt.Println(conn.RemoteAddr().String()+": "+string(b[:n]))
      conn.Write([]byte("收到"+string(b[:n])))
   }
}

client

package main

import (
   "bufio"
   "fmt"
   "net"
   "os"
)
func main() {
   tcpAddr ,_:= net.ResolveTCPAddr("tcp",":9999")
   conn, _ := net.DialTCP("tcp", nil, tcpAddr)
   for {
      reader := bufio.NewReader(os.Stdin)
      b, _:=  reader.ReadString('\n')
      conn.Write([]byte(b))
      re := make([]byte, 1024)
      n, _ := conn.Read(re)
      if n == 0 {
         fmt.Println("发送失败")
      }
      fmt.Println(conn.RemoteAddr().String()+": "+string(re[:n]))
   }
}

RPC包

注意事项:

  • Go的RPC只支持go写的系统
  • 对函数的要求
    • 首字母必须大写(公开的)
    • 必须只有两个参数第一个参数是接收的参数,第二个参数是返回给客户端的参数,第二个参数必须是指针类型的
    • 函数还要有一个返回值error

举例: func(t *T) MethonName(argType T1, replyType *T2) error{}

dome

server

package main

import (
   "fmt"
   "net"
   "net/http"
   "net/rpc"
)

type Server struct {
}

type Req struct {
   NumOne int
   NumTwo int
}
type Res struct {
   NumTotal int
}

func (s *Server) Add(req Req, res *Res) error {
   res.NumTotal = req.NumTwo + req.NumOne
   return nil
}

func main() {
   rpc.Register(new(Server))
   rpc.HandleHTTP()
   l, err := net.Listen("tcp",":8888")
   if err != nil {
      fmt.Println("失败!")
      return
   }
   http.Serve(l, nil)
}

client

package main

import (
	"fmt"
	"net/rpc"
	"time"
)

type Req struct {
	NumOne int
	NumTwo int
}
type Res struct {
	NumTotal int
}

func main() {
	req := Req{
		NumOne: 1,
		NumTwo: 2,
	}
	var res Res
	client, err := rpc.DialHTTP("tcp", ":8888")
	if err != nil {
		fmt.Println("错误!")
		return
	}
    //ca := client.Call("Server.Add", req, &res)
	ca := client.Go("Server.Add", req, &res, nil)
	//fmt.Println("这里会在拿到返回结果前面执行,可以做很多事情")
	for  {
		select {
		case <-ca.Done:
			fmt.Printf("Arith: %d*%d=%d", req.NumOne, req.NumTwo, res)
			return
		default:
			time.Sleep(1 * time.Second)
			fmt.Println("等待")
		}
	}
}

websocket

是http的一种升级模式

使用包

require github.com/gorilla/websocket v1.5.0

基础http

package main

import "net/http"

func handler(w http.ResponseWriter, r *http.Request) {

}

func main() {
   http.HandleFunc("/", handler)
   http.ListenAndServe(":8888", nil)
}

升级结构

使用

var UP = websocket.Upgrader{}

Upgrader结构体

	HandshakeTimeout: 0, //握手时间0为不限制
	ReadBufferSize:   1024, //以字节为单位的IO缓存区,如果为0,则使用http服务器分配的缓冲区
	WriteBufferSize:  1024, //以字节为单位的IO缓存区,如果为0,则使用http服务器分配的缓冲区
	WriteBufferPool:  nil, //用于写操作的缓冲池
	Subprotocols:     nil, //按照顺序指定服务器支持的协议
	Error: nil, // 指定用于生成http错误响应的函数
	CheckOrigin: nil, // 对过来的请求校验用
	EnableCompression: false, // 指定服务器是否尝试根据进行协商消息压缩

demo

服务端

package main

import (
	"bufio"
	"fmt"
	"github.com/gorilla/websocket"
	"log"
	"net/http"
	"os"
	"strings"
)

var (
	UP = websocket.Upgrader{
		ReadBufferSize:  1024, //以字节为单位的IO缓存区,如果为0,则使用http服务器分配的缓冲区
		WriteBufferSize: 1024, //以字节为单位的IO缓存区,如果为0,则使用http服务器分配的缓冲区
	}
	// 注册的客户端连接
	clients = make(map[string]*websocket.Conn)
	// 用于广播消息的通道
	broadcast = make(chan string)
)

func handler(w http.ResponseWriter, r *http.Request) {
	conn, err := UP.Upgrade(w, r, nil)
	if err != nil {
		log.Fatal(err)
		return
	}

	fmt.Println(r.RemoteAddr + "建立连接")

	// 关闭WebSocket连接
	defer conn.Close()

	// 注册客户端连接
	_, mp, err := conn.ReadMessage()
	if err != nil {
		log.Fatal(r.RemoteAddr + "发生错误")
	}
	clients[string(mp)] = conn

	//没有错误,建立for持续的拿消息
	for {
		_, p, e := conn.ReadMessage()
		if e != nil {
			break
		}
		conn.WriteMessage(websocket.TextMessage, []byte("服务器接收成功"))
		fmt.Println(string(mp) + " 发出消息 -- " + string(p))
		broadcast <- string(mp) + "/;;/" + string(p)

	}
	defer conn.Close()
	fmt.Println("服务关闭")
}

func sand() {
	for {
		read := bufio.NewReader(os.Stdin)
		l, _, _ := read.ReadLine()
		//给每一个客户端发消息
		broadcast <- "server" + "/;;/" + string(l)
	}

}

// 广播消息
func handleMessages() {
	for {
		// 从广播通道中读取消息
		message := <-broadcast
		m := strings.Split(message, "/;;/")

		s := strings.Split(m[1], ":")

		if len(s) == 1 {
			// 将消息发送给所有连接的客户端
			for name, client := range clients {
				err := client.WriteMessage(websocket.TextMessage, []byte("服务器群发消息--- "+m[1]))
				if err != nil {
					log.Println(err)
					client.Close()
					delete(clients, name)
				}
			}
		} else {
			if clients[s[0]] != nil {
				//找到发送对象且在线
				err := clients[s[0]].WriteMessage(websocket.TextMessage, []byte(m[0]+" 发送的消息 "+s[1]))
				if err != nil {
					log.Println(err)
					clients[s[0]].Close()
					delete(clients, s[0])
				}
			}

		}

	}
}

func main() {
	//启动广播进程
	go handleMessages()

	// 服务端发送消息给全部在线客户端
	go sand()
	http.HandleFunc("/", handler)
	err := http.ListenAndServe(":8888", nil)
	if err != nil {
		log.Fatal("ListenAndServe: ", err)
	}
}

客户端

package main

import (
   "bufio"
   "fmt"
   "github.com/gorilla/websocket"
   "log"
   "os"
   "strings"
)

func main() {
   dl := websocket.Dialer{}
   conn, _, err := dl.Dial("ws://127.0.0.1:8888", nil)
   if err != nil {
      log.Fatal(err)
      return
   }
   conn.WriteMessage(websocket.TextMessage, []byte("crabin"))
   go sand(conn)
   for {
      _, p, e := conn.ReadMessage()
      if e != nil {
         break
      }
      fmt.Println(string(p))
   }

}

func sand(conn *websocket.Conn) {
   for {
      read := bufio.NewReader(os.Stdin)
      l, _, _ := read.ReadLine()
      if strings.Contains(string(l), ":") {
         conn.WriteMessage(websocket.TextMessage, l)
      } else {
         fmt.Println("请输入正确的发送格式---目标:信息")
      }

   }
}

缺省

切片

数组的一部分,切片数据改变,数组也改变

a := []int{1,2,3}
cl := a[2:]
fmt.Println(cl)
cl[0] = 5
fmt.Println(cl)
fmt.Println(a)

image-20230213213324651

注意

常识1

包里面使用大写开头的变量或者方法才能被其他文件引用(公有),否则就只能自己调用(即私有)

标签:err,int,fmt,学习,Println,func,go,conn
From: https://www.cnblogs.com/crabin/p/17131418.html

相关文章

  • 230217 关于英语学习的目标
    最近几天,你在听larry的英语课程.通过larry的介绍,你有了一些新的感触与理解.尤其是你关于你的英语的学习目标,你值得去深入思考,同时,不断校正与优化你的目标.之前,你......
  • SpringBoot学习记录(1)——@Autowired在集合上的作用
    publicclassSmsHandlerextendsBaseHandlerimplementsHandler{@AutowiredprivateMap<String,SmsScript>smsScripts;//......}例如如上,一个......
  • RSA学习之旅------2023.2.16
    一,RSA算法简单描述1,任意选取两个不同的大素数p和q计算乘积2,任意选取一个大整数e,满足 ,整数e用做加密钥(注意:e的选取是很容易的,例如,所有大于p和q的素数都可用)3,确定的......
  • 万字长文带你入门增量学习
    前言本文介绍了引入增量学习的必要性,继而引出最新的三种能够有效利用有效标签数据范式的增量学习方法。其次,针对这三大范式进行一个全面的调研,让大家对这个领域的整个发......
  • 处理坑爹的 GOPATH (系统变量和Goland)
    学习go的过程中发现写好的代码放到自己电脑上发现跑不起来,说是导包失败,晚上睡觉看书的时候突然想到GOPATH,这是go语言特有的环境变量,是不是这个东西搞的鬼。设置gopath也是......
  • 半监督学习
    1  相关概念1.1 半监督学习的定义同时利用有标注数据和无标注数据学习 1.2 半监督分类/回归给定标注数据和无标注数据,学习得到一个分类器f,要求该分类器f比只......
  • 如何在机器学习中处理长尾数据分布?丨曼孚科技
    如果代码质量是区分软件系统好坏的标准,那么数据质量便是区分AI系统智能化的标准。对模型来说,使用正确的数据不可或缺。而实际训练中,常出现场景数据分布不均衡的现象,长尾数......
  • scrum工具leangoo看板切换时间线视图上线。
    企业需要开发一个项目,可以制作时间线进行管理,以便参与者和管理者了解项目的时间进度。项目进行到哪一步,参与者有哪些,责任人是谁,这些都可以通过时间线进行展示。Leangoo......
  • 机器学习--2神经网络
    神经网络神经网络模型神经网络与线性回归的思想类似,然后添加相应的激活函数输出对应的结果。经典的神经网络有以下三个层次组成:输入层(inputlayer),隐藏层(hiddenla......
  • Django Paginatior分页,页码过多,动态返回页码,页码正常显示
    问题:当返回数据较多,如设置每页展示10条,数据接近200条,返回页码范围1~20,前端每个页码都显示的话,就会出现页码超出当前页面,被遮挡的页码无法操作和显示不美观;代码优化:在使......