项目中遇到的问题回顾

1、原生的json包无法解析未知格式的json数据

解决方法:使用第三方的simpleJson包解析数据

2、空map不能赋值
func main() {
    var t map[string]string
    t["name"]="zhangsan"   //报错
}

解决方法:先初始化map

func main(){
    t := make(map[string]string)
    t["name"]="zhangsan"   //正常
}

3、如何使用go map同时储存多种类型的value

如下定义:只能储存string类型的value。

mapStr := make(map[string]string)

如下定义:只能保存int类型的value。

mapInt := make(map[string]int)

如下定义:可以保存string、int等不同类型的value。

mapInterface := make(map[string]interface{})

mu :=  make([]map[string]interface{},0)
a1 := map[string]interface{} {"id": 1, "parentId": 0, "createTime": "2020-02-02T06:50:36.000+0000", "title": "商品", "level": 0,
"sort": 0, "name": "pms", "icon": "product", "hidden": 0}

a2 := map[string]interface{} {"id": 2, "parentId": 1, "createTime": "2020-02-02T06:51:50.000+0000", "title": "商品列表", "level": 1,
"sort": 0, "name": "product", "icon": "product-list", "hidden": 0}

mu = append(mu, a1)
mu = append(mu, a2)
3、url.Values添加重名Key不会覆盖,而是会变成一个数组。
 var BaseArgs = url.Values{}
 BaseArgs.Add("key","value1")
 BaseArgs.Add("key","value2")    //value1和value2会以数组的形式存储起来

正确用法:

var BaseArgs = url.Values{}
BaseArgs.Set("key","value1")
BaseArgs.Set("key","value2")    //value2覆盖value1
4、打开的网络请求必须关闭,否则内存泄露多了程序会崩溃
req, err := http.NewRequest(method, requrl, nil)
_ = "错误校验"
if err != nil {
	return res, err
}
client := &http.Client{}
	resp, err := client.Do(req)
if resp != nil {
	defer resp.Body.Close()   //此处必须关闭,否则开的多了程序会崩溃
}
if err == nil { //请求成功
		statuscode := resp.StatusCode
		body, _ := ioutil.ReadAll(resp.Body)
		fmt.Println(string(body))
		fmt.Println(statuscode)
}
5、多线程同时修改同一个变量,程序会崩溃。

需要加锁:

import sync
var lock sync.Mutex // 定义一个互斥锁

lock.Lock()
//操作公共变量
lock.Unlock()

6、多协程并发时,某个协程崩溃会导致整个进程崩溃

解决方法:为相关方法添加异常捕获,确保程序不崩溃

func function_name()() {

	defer func() { // 必须要先声明defer,否则不能捕获到panic异常
		if err := recover(); err != nil {
			b := fmt.Sprint(err)
			writeExceptionLog(b)
		}
		// fmt.Println("d")
	}()
	
	
	_:"这下面编写你的业务代码"
}
7、main方法结束之后,在main方法中启动的协程也会自动结束

问题现象:

func main() {
    go hello() // 启动另外一个goroutine去执行hello函数
    fmt.Println("main goroutine done!")
}
执行结果只打印了main goroutine done!,并没有打印hello函数中的内容

问题原因:

在程序启动时,Go程序就会为main()函数创建一个默认的goroutine。

当main()函数返回的时候该goroutine就结束了,所有在main()函数中启动的goroutine会一同结束,main函数所在的goroutine就像是权利的游戏中的夜王,其他的goroutine都是异鬼,夜王一死它转化的那些异鬼也就全部死掉了。

解决方法一:

想办法让main函数等一等hello函数,最简单粗暴的方式就是time.Sleep了。但是这样不好。

解决方法二:协程同步

import	"sync"

func hello(wg *sync.WaitGroup) {
    _="这里写你的业务"
   wg.Done()
}

func main() {
    var wg sync.WaitGroup
	wg.Add(1)
	go hello( &wg)
	fmt.Println("main goroutine done!")
	// main方法等待hello结束之后再推出
	wg.Wait()
}
8、理解进程、线程、协程
  • 一个进程可以有多个线程(至少有一个主线程),
  • 一个线程上面可以挂载多个协程,通过调度算法合理的分配各个协程的执行时间,
  • 协程和线程是多对多的关系。一个线程上面可以同时挂载多个协程。一个协程X1刚开始挂载在线程B上面执行,一段时间后,线程A上面的协程全部执行完了,但是线程B上面还有好几个协程没有执行完毕,协程X1有可能会被重新分派到线程A上去执行。
  • 操作系统对协程是完全没有感知的。
  • 一个GO进程默认会开启和当前机器CPU数量一致的线程数,可以手动调节最大线程数。
9、go语言代码格式化神器
go fmt myProject
10、switch 的break问题

switch无需显式执行break,每个case语句后面自带break,可以通过fallthrough强制执行紧挨着的下一个case里面的语句(无论紧挨着的下一个case的判断条件是否成立),default语句块中不允许使用fallthrough语句。

eg:

 t:=1
 switch  {
    case t<4:
        fmt.Println("The integer was <= 4")
        fallthrough   //强制执行下面的<=5的输出
    case t<0:
        fmt.Println("The integer was <= 5")
        fallthrough   //强制执行下面的<=6的输出
    case t<6:
        fmt.Println("The integer was <= 6")
         fallthrough  //强制执行下面的default语句
    default:
        fmt.Println("default case")
  }

最终所有输出语句都会被执行。

11、跳出代码块 for、 swtich、select

方法:使用break。用法和其他语言一样

注意:只跳出当前代码块,只跳出当前循环。

12、go 语言当中channel有什么特点,需要注意什么?

如果给一个 nil 的 channel 发送数据,会造成永远阻塞
如果从一个 nil 的 channel 中接收数据,也会造成永久阻塞
给一个已经关闭的 channel 发送数据, 会引起 pannic
从一个已经关闭的 channel 接收数据, 如果缓冲区中为空,则返回一个零值
无缓冲的 channel是同步的,而有缓冲的channel是非同步的。

13、simpleJson中单引号和双引号不一样
simplejson_Json.Get("status").MustInt()   //正确
simplejson_Json.Get('status').MustInt()   //错误,取json中的键值对的值时,传递的键名称必须使用双引号包裹。
14、变量作用域

同一个方法内的,一个代码块里面申请的变量,在该代码块外面不能访问。

eg:

func main() {
    var t=1
	if t>3{
	   var a=true
	}else{
	   var a=true
	}
	
	fmt.Println("a=",a)  //此处报错,提示找不到变量a
	
}
15、同样的Linux命令,手动执行和代码执行结果不一样

现象:手动执行以下命令会按照预期创建分区为1 、副本为3的topic; 使用代码执行以下命令会生成分区为1、副本为1的topic。

"ansible kafka1 -m shell -a'/root/kafka/kafka_2.12-2.3.0/bin/kafka-topics.sh  --create --replication-factor 3 --partitions 1 --zookeeper localhost:2181  --topic test_topic'"

解决方法:

将该命令写入文件,然后执行文件。就得到了和预期一致的结果。

16、自定义的Flask服务默认使用http访问

问题现象:使用https方式访问自定义的Flask服务失败。

解决方法:改用http方式访问。

17、读写锁的应用

go语言中2个协程同时读和写同一个变量会导致程序崩溃(该异常无法被defer捕获),需要加锁。
需求:多个协程可以同时读一个公共变量,但是有读的时候不能有写。
实现:

import (
   
    "sync"
   
)

//全局变量
var count int   //定义全局公共变量
var rLock sync.RWMutex  //定义读写锁

func Read() {
 for{
    rLock.RLock()  //加读锁(读锁可以获取多个。当有写锁没有释放时,无法获取读锁。)
    fmt.Printf("读取count数据", count)
    defer rLock.RUnlock()  //释放读锁
 }
}

func Write() {
   for{
    rLock.Lock()  //加写锁(写锁全局只能获取一个。且有读锁没有释放时,无法获取写锁)
    count += 1
    fmt.Printf("写 count 数据 ",   count)
    rLock.Unlock() //释放写锁
   }
}

func main(){
    go Read()
    go Read()
    go Write()
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值