• golang 琐碎知识


    golang 琐碎知识(持续进行)

    时间格式

    time.now.Format("2006-01-02T 15:04:05")
    
    • 1

    make声明切片bug

    Golang:statusList := make([]*model.StatusList, 6)
    会声明一个长为6的null切片,使用append添加时不会将null覆盖掉
    
    • 1
    • 2

    去掉切片末尾元素

    segments[:len(segments)-1]
    
    • 1

    互斥锁

    • 带缓存的channel

      var signal = make(chan struct{}, 1)
      func Test() {
      	signal <- struct{}{}
      	defer func() {
      		<-signal
      	}()
      }
      
      • 1
      • 2
      • 3
      • 4
      • 5
      • 6
      • 7

      可以实现与mutex相同的功能,进入函数向带一个缓存的channel中写入一个元素,导致其处于阻塞状态,其余协程在进入该函数并写入的时候会阻塞等待其释放。该函数执行完毕,则会释放该缓存channel,其余协程便可依次进入执行。

    • sync.mutex

      var mu sync.Mutex
      func Test() {
      	mu.Lock()
      	defer mu.Unlock()
      }
      
      • 1
      • 2
      • 3
      • 4
      • 5

      与上述具有相同的作用。

    • 注意:全局的变量i,其自增使用需要特别注意

      var signal = make(chan struct{}, 1)
      
      func IsPortAvailable(port int) bool {
      	address := fmt.Sprintf("%s:%d", "0.0.0.0", port)
      	listener, err := net.Listen("tcp", address)
      	if err != nil {
      		log.Printf("port %s is taken: %s", address, err)
      		return false
      	}
      
      	defer listener.Close()
      	return true
      }
      
      var avaiblePort = 1000
      
      func GetAvailablePort() int {
      	var j int
      	signal <- struct{}{}
      	for {
      		avaiblePort++
      		j = avaiblePort
      		if IsPortAvailable(j) {
      			break
      		}
      	}
      	<-signal
      
      	return j
      }
      
      • 1
      • 2
      • 3
      • 4
      • 5
      • 6
      • 7
      • 8
      • 9
      • 10
      • 11
      • 12
      • 13
      • 14
      • 15
      • 16
      • 17
      • 18
      • 19
      • 20
      • 21
      • 22
      • 23
      • 24
      • 25
      • 26
      • 27
      • 28
      • 29
      • 30

      上述写法没有问题,需要注意不能直接返回全局变量avaiblePort,因为这段代码只能保证在临界区内部可以保证自增是互斥的,但是除了临界区之后该变量会被其它协程抢占,所以在return的时候,该变量已经发生了改变,有可能是1001、1002。所以正确的做法是将该可用的全局变量赋值给一个局部变量,然后返回。并且注意该例子是获取可用端口的并发互斥实现,所以需要赋值给局部变量j之后再判断该端口是否可用,如果判断完再赋值,也会导致并发互斥失效。

    panic recover

    参考:https://geektutu.com/post/gee-day7.html

    core dump 打印堆栈信息

    https://blog.csdn.net/xmcy001122/article/details/105665732?spm=1001.2101.3001.6650.3&utm_medium=distribute.pc_relevant.none-task-blog-2%7Edefault%7ECTRLIST%7ERate-3-105665732-blog-103850401.pc_relevant_3mothn_strategy_recovery&depth_1-utm_source=distribute.pc_relevant.none-task-blog-2%7Edefault%7ECTRLIST%7ERate-3-105665732-blog-103850401.pc_relevant_3mothn_strategy_recovery&utm_relevant_index=3

    任务队列,执行异步任务时需要考虑异步worker

    https://lailin.xyz/post/go-training-03.html

    编写实例:https://gitee.com/huoyingwhw/go_async_task

    异步任务,如果需要使用 goroutine 时,应该使用同一的 Go 函数进行创建,这个函数中会进行 recover ,避免因为野生 goroutine panic 导致主进程退出,recover只会对当前goroutine生效,对子goroutine不起作用

    func Go(f func()){
        go func(){
            defer func(){
                if err := recover(); err != nil {
                    log.Printf("panic: %+v", err)
                }
            }()
    
            f()
        }()
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11

    panic两种出现情况:

    • 没有预期的一种异常的发生,自动抛出
    • 预期的抛出

    设计模式

    https://lailin.xyz/post/singleton.html

    任务队列machinery使用与源码剖析

    https://cloud.tencent.com/developer/article/1169675

    redis 消息队列 golang

    https://juejin.cn/post/7058699128284381221

    golang build编译

    [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-UQQCZ6sK-1670136444071)(C:\Users\zzy\AppData\Roaming\Typora\typora-user-images\image-20221019132303376.png)]

    golang 消息队列

    NSQ,介绍:https://cloud.tencent.com/developer/article/1896007

    golang 使用sqlite

    https://learnku.com/docs/build-web-application-with-golang/053-uses-the-sqlite-database/3183

    golang 分布式数据流转

    https://juejin.cn/post/7088262766779170853

    go test 添加性能打印

    go test -benchmem -bench=“.” -v singleton.go singleton_test.go

    多态

    什么是多态?

    概念:同一操作作用于不同的对象,可以有不同的解释,产生不同的执行结果,这就是多态性。简单的说:就是用基类的引用指向子类的对象。

    为什么要用多态呢?

    原因:我们知道,封装可以隐藏实现细节,使得代码模块化;继承可以扩展已存在的代码模块(类);它们的目的都是为了——代码重用。而多态除了代码的复用性外,还可以解决项目中紧偶合的问题,提高程序的可扩展性.。耦合度讲的是模块模块之间,代码代码之间的关联度,通过对系统的分析把他分解成一个一个子模块,子模块提供稳定的接口,达到降低系统耦合度的的目的,模块模块之间尽量使用模块接口访问,而不是随意引用其他模块的成员变量。

    多态有什么好处?

    有两个好处:

    • 1、应用程序不必为每一个派生类编写功能调用,只需要对抽象基类进行处理即可。大大提高程序的可复用性。//继承
    • 2、派生类的功能可以被基类的方法或引用变量所调用,这叫向后兼容,可以提高可扩充性和可维护性。 //多态的真正作用,

    golang crypto/rsa 使用教程

    https://zhuanlan.zhihu.com/p/384595092

    golang 同步包

    go mod tidy

    面向对象的编程思维详解

    https://github.com/aceld/golang/blob/main/6%E3%80%81%E9%9D%A2%E5%90%91%E5%AF%B9%E8%B1%A1%E7%9A%84%E7%BC%96%E7%A8%8B%E6%80%9D%E7%BB%B4%E7%90%86%E8%A7%A3interface.md#6%E9%9D%A2%E5%90%91%E5%AF%B9%E8%B1%A1%E7%9A%84%E7%BC%96%E7%A8%8B%E6%80%9D%E7%BB%B4%E7%90%86%E8%A7%A3interface

    //实现架构层(基于抽象层进行业务封装-针对interface接口进行封装)
    func BankerBusiness(banker AbstractBanker) {
    	//通过接口来向下调用,(多态现象)
    	banker.DoBusi()
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5

    解决exec.Start() 产生僵尸进程无法回收

    // 执行shell
    func ExecShell(s string) error {
    	//execComand := func() error {
    	//	err := exec.Command("/bin/bash", "-c", s).Start()
    	//	logging.Infof("utils.ExecShell 脚本: %s\n", s)
    	//	if err != nil {
    	//		return err
    	//	}
    	//
    	//}
    
    	ctx := context.Background()
    
    	cmd := exec.CommandContext(ctx, "/bin/bash", "-c", s)
    
    	cmd.SysProcAttr = &syscall.SysProcAttr{}
    
    	err := cmd.Start()
    	if err != nil {
    		return err
    	}
    
    	logging.Infof("utils.ExecShell 脚本: %s\n", s)
    
    	// 监听进程wait,回收子进程
    	errCmdCh := make(chan error, 1)
    
    	go func() {
    		go func() {
    			errCmdCh <- cmd.Wait()
    		}()
    		for {
    			select {
    			case <-ctx.Done():
    				logging.Infof("utils.ExecShell ctx.done\n")
    				pid := cmd.Process.Pid
    				if err := syscall.Kill(-1*pid, syscall.SIGKILL); err != nil {
    					return
    				}
                    //想要杀死整个进程组,而不是单个进程,需要传递负数形式。
    			case err := <-errCmdCh:
    				if err != nil {
    					logging.Errorf("utils.ExecShell 脚本: s, errCmdCh error %+v \n", s, err)
    				}
    				return
    			default:
    				time.Sleep(1 * time.Second)
    			}
    		}
    	}()
    
    
    	return nil
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54

    参考:https://blog.csdn.net/af2251337/article/details/125357359

    注意指针问题

    在实际开发中遇到了一个比较棘手的问题,在循环外声明了一个对象,然后循环内更改值并把地址赋值给另外一个对象,然后将该对象append一个切片中,因为是指针赋值,所以在该切片中存在的全是同一个地址,因此值也是一样的,都是最后一次赋值的内容。

    golang 异步任务需传参

    永不停息!!!:
    大佬请教一下,我现在使用go关键字实现了异步任务,但是我这个地方有个疑问,就是taskResultService 这个对象要不要当作入参传入go关键字开启的这个匿名函数里面,像我这样使用有没有问题呀
    代码:
    func A(c *gin.Context) {
    	var err error
    	taskResultService := &service.TaskResultService{}
    	err = c.ShouldBindJSON(&taskResultService.M)
    	if err != nil {
    		logging.Errorf("api.HandleTaskResult: bind json 失败, err: %v\n", err)
    		c.JSON(400, utils.SyncErrorResponse(err))
    		return
    	}
    	// sync response
    	c.JSON(0, utils.SyncSuccessResponse())
    	// async execute
    	go func() {
    		logging.Infof("api.HandleTaskResult: 开始获取任务结果目录 [%s]\n", taskResultService.M.BaseInfo.TaskId)
    		// auth token
    		ttSecret, err := oauthc.Agent2ServerAuthGetToken(taskResultService.M.BaseInfo.TaskId)
    		if err != nil {
    			logging.Errorf("api.HandleTaskResult: 从mysql获取token失败, err: %v\n", err)
    			return
    		}
    	}()
    }:
    你这个可以不用
    
    永不停息!!!:
    这样使用的话,默认就把taskResultService这个对象传过去了吗,这里面的底层逻辑是怎样的,能详细说一下吗
    
    ㅤ:
    不是默认传进去了  你可以简单理解为for大括号里用了大括号外的变量 是一个原理
    
    永不停息!!!:
    如果存在并发的话,有多个taskResultService对象,会不会引起参数错乱
    
    ㅤ:
    会 如果你在for 循环里 开协程  用for语句里的变量。你会发现所有协程用的是for语句的最后一个值
    
    那就只能传入参数来避免咯
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42

    golang 释放互斥锁

    //var mutex sync.Mutex
    //
    //func MutexText() {
    // defer func() {
    //    if err := recover(); err != nil {
    //       log.Println(err)
    //    }
    // }()
    // log.Println("尝试获得锁")
    // flag := mutex.TryLock()
    // defer mutex.Unlock()
    // log.Println(flag)
    // log.Println("获得锁")
    // time.Sleep(time.Second*3)
    // panic("错误")
    //
    //}
    
    func main() {
    	//for i:=0; i<2; i++ {
    	//	log.Println("123")
    	//	go MutexText()
    	//}
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25

    er(); err != nil {
    // log.Println(err)
    // }
    // }()
    // log.Println(“尝试获得锁”)
    // flag := mutex.TryLock()
    // defer mutex.Unlock()
    // log.Println(flag)
    // log.Println(“获得锁”)
    // time.Sleep(time.Second*3)
    // panic(“错误”)
    //
    //}

    func main() {
    //for i:=0; i<2; i++ {
    // log.Println(“123”)
    // go MutexText()
    //}
    }

    结构体嵌套序列化

    package main
    
    import (
    	"encoding/json"
    	"log"
    )
    
    type Person struct {
    	Name string `json:"name"`
    	Author
    }
    
    type Author struct {
    	Id string `json:"id"`
    }
    
    func main() {
    	p := new(Person)
    	p.Name = "zzy"
    	p.Id = "123"
    	body, err := json.Marshal(p)
    	if err != nil {
    		log.Fatal(err)
    	}
    	log.Println("marshal : ", string(body))
    }
    // output:
    2023/01/16 10:49:51 marshal :  {"name":"zzy","id":"123"}
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28

    闭包

    golang的闭包是指函数内部声明了一个匿名函数,改函数是环境+匿名函数的组合,是引用了变量的函数,因此变量会常驻内存,不会立即销毁。

  • 相关阅读:
    PhpStorm环境配置与应用
    自学CFD:我在实习岗速成无人机设计和仿真的故事
    计算机系统(10)----- 进程通信
    Ant-Design-Vue 动态表头
    Nodejs安装和下载
    Spring 补充@Component 和 注入技巧 问题
    c++四种强制类型转换
    ACM. HJ70 矩阵乘法计算量估算 ●●
    openRPA开源项目源码编译
    市场拓展招聘:完整指南
  • 原文地址:https://blog.csdn.net/qq_35324057/article/details/128172524