社区微信群开通啦,扫一扫抢先加入社区官方微信群
社区微信群
使用定时器
t := time.NewTicker(1 * time.Second)
// 第一种方式
for {
select {
case <-t.C:
fmt.Println("hello ")
default:
time.Sleep(100 * time.Millisecond)
}
}
// 第二种方式
for _ = range t.C {
fmt.Println("hello")
}
生成指定长度的随机字符串
rand.Seed(time.Now().UnixNano())
data := make([]byte, 32)
rand.Read(data)
fmt.Println(hex.EncodeToString(data))
slice转array
func main() {
var arr [10]int
s := []int{1, 2, 3, 4, 5}
copy(arr[:], s)
fmt.Println(arr)
}
原理解析:arr[:]是一个引用了底层数组arr的slice,长度和容量与数组的长度相同,也就是说这两个元素共用一块内存。所以当copy()时,改变了slice中的元素值,而数组的改变可以说是一种副效应(side-effect)。
通过append函数删除slice中的元素,是否会重新分配内存的问题
package main
import "fmt"
func main() {
s := []int{1, 2, 3, 4, 5}
a := s
fmt.Println(a)
s = append(s[:1], s[2:]...)
// 修改s[0],如果a的数据也变化了,那么说明通过append()
// 删除slice中的数据并没有重新分配内存空间
s[0] = 12
fmt.Println(a, s)
}
// output
[1 2 3 4 5]
[6 3 4 5 5] [6 3 4 5]
交换两个元素的值,而不需要中间变量
package main
import "fmt"
func main() {
i := 1
j := 2
i = i ^ j
j = i ^ j
i = i ^ j
fmt.Println(i, j)
}
// 规律是 i ^ i = 0
如果当前函数或者其调用栈上游,有处理panic的recover(),panic()就不会输出,更不会终止程序,但是panic()的确执行了。
package main
import "fmt"
func main() {
test()
fmt.Println("here is waiting")
}
func test() {
defer func() { // 可以在panic()上游的任何位置定义,都会拦截
if r := recover(); r != nil {
fmt.Println("is there")
}
}()
base()
}
func base() {
panic("this is a error")
}
从一个给定的slice获取空的slice
package main
import "fmt"
func main() {
s := []int{1, 2, 3, 4, 5}
sEmpty := s[0:0]
fmt.Println(sEmpty)
}
[]byte转十六进制
b := []byte{12, 34, 89}
// 以下会得到相同的输出
fmt.Printf("%xn", b)
fmt.Println(hex.EncodeToString(b)
大端法与小端法
给定一个十六进制数0xOA0B0C0D,以下给出了在两种不同方法下内存的排序方式,内存地址从左向右依次增大。上方为小端法表示、下方为大端法表示。
概念:
比较好记的是大端法数字的书写顺序与内存排列顺序一致
![大端法、小端法][image-1]
在golang中将一个数字转换成byte,在小端法中,会解析低内存地址的一个byte,作为输出.
func main() {
var a uint32 = 0x0A0B0C0D
fmt.Println(byte(a))
}
slice copy时注意事项
第一种情况为空slice,因此在使用Copy()方法时,目标slice长度一定要指定为需要copy的长度
func main() {
s := make([]int, 0, 5) // error
//s := make([]int, 4) // correct 4 or 5 ok
c := []int{1, 2, 3, 4, 5}
copy(s, c)
fmt.Println(s)
}
标识符冲突
同一个包内不能出现变量名、常量名、函数名两两冲突的情况,如下情况编译失败:
package main
import "fmt"
const conflict = 38
var conflict = 34
func conflict() {
fmt.Println(conflict)
}
func main() {
fmt.Println(conflict)
conflict()
}
求两个时间的间隔,会参数负数的情况
func main() {
t1 := time.Now()
t2 := time.Now()
fmt.Println(t1.Sub(t2)) // 负数
t3 := time.Unix(math.MaxInt64, 0) // 属于越界的情况
fmt.Println(t3.Sub(t1))
}
// output
-657ns
-2562047h47m16.854775808s
所以在使用time包的Sub()函数时,要将结果与0进行比较
now := time.Now()
lastAttempt := now.Sub(ka.lastattempt) // 如果差值过大,就会形成负值
if lastAttempt < 0 {
lastAttempt = 0
}
nil slice可以直接使用append()增加元素
func main() {
var b []byte
source := []byte{1, 2, 3}
b = append(b, source...)
fmt.Println(b)
}
golang set循环控制及返回值
func (ba *BlockAssembler) isStillDependent(te *mempool.TxMempoolEntry) bool {
setParent := blockchain.GMemPool.GetMemPoolParents(te)
ret := false
setParent.Each(func(item interface{}) bool {
if !ba.inBlock.Has(item.(*mempool.TxMempoolEntry)) {
ret = true // control function result
return false // control loop
}
return true // control loop
})
return ret // return function result
}
select默认是堵塞的,必须有一个分支执行才能出去。
func main() {
a := make(chan int, 1)
c := make(chan int, 1)
go func() {
time.Sleep(1 * time.Second)
a <- 1
c <- 2
}()
// for next {
select {
case <-a:
fmt.Println("a")
case <-c:
fmt.Println("c")
}
}
// 如果所有通道中都没有内容,可以使用default语句,继续程序的执行。不过要看业务需求。
func main() {
a := make(chan int, 1)
c := make(chan int, 1)
go func() {
time.Sleep(1 * time.Second)
a <- 1
c <- 2
}()
// for next {
select {
case <-a:
fmt.Println("a")
case <-c:
fmt.Println("c")
default:
fmt.PrintLn("default")
}
}
switch结构中default语句的执行与其所在位置无关
package main
import "fmt"
func main() {
a := 232
switch a {
default:
fmt.Println("default")
case 1:
fmt.Println(1)
case 232:
fmt.Println("ok")
}
}
遍历一个无缓冲的channel,不会在从channel中读取一个元素就退出(for range 结构); for range 的终止条件是通道关闭,和channel有无缓冲无关。
package main
import (
"fmt"
"time"
)
func main() {
c := make(chan int)
go func() {
i := 0
for {
c <- i
i++
time.Sleep(1 * time.Second)
}
}()
go func() {
for v := range c {
fmt.Println(v)
}
}()
time.Sleep(10 * time.Second)
close(c)
}
len()函数在channel的应用。当chanel为无缓冲的时候,len()在任何情况下都会返回0; 当channel为有缓冲的情况下,len()会返回当前缓冲中的元素个数,增大或减小取决于生产者和消费者的速度。总之,len()函数在channel中返回的是缓冲区的元素个数。
package main
import (
"fmt"
"time"
)
func main() {
c := make(chan int, 5) // 修改为无缓冲的channel尝试一下
// producer
go func() {
i := 0
for {
c <- i
i++
time.Sleep(1 * time.Second)
}
}()
// consumer
go func() {
for v := range c {
fmt.Println(v)
time.Sleep(2 * time.Second)
}
}()
// watcher
go func() {
for {
fmt.Println("channel length:", len(c))
time.Sleep(500 * time.Millisecond)
}
}()
time.Sleep(30 * time.Second)
}
goto、continue、break语句实现代码跳转
// 既可以往前跳,也可以往后跳
func main() {
previous:
fmt.Println("previous")
i := 0
if i == 0 {
goto next
}
next:
fmt.Println("next")
goto previous
}
// break跳转至指定标签后,将不执行标签对应的for循环。需要注意的是:标签只能定义在for循环之前。
func main() {
i := 0
loop:
for i <= 10 {
i++
fmt.Println("break")
break loop
}
fmt.Println("finish, and i ==", i)
}
// output:
break
finish, and i == 1
// continue跳转到指定标签后,如果for循环条件满足仍然会执行for循环,直到条件不满足为止。需要注意的是:标签只能定义在for循环之前。
func main() {
i := 0
loop:
for i <= 10 {
i++
fmt.Println("continue")
continue loop
}
fmt.Println("finish, and i ==", i)
}
// output:
continue
continue
continue
continue
continue
continue
continue
continue
continue
continue
continue
finish, and i == 11
switch case for type asert:
func main() {
var i interface{}
a := 3
i = a
switch t := i.(type) {
case int:
fmt.Println(t)
default:
fmt.Println("default")
}
}
//output:
3
<- c
被触发的条件:
1、通道中有数据
2、通道被关闭
func main() {
c := make(chan struct{})
//c := make(chan struct{}, 100)
go func() {
time.Sleep(5 * time.Second)
close(c)
}()
for {
if interruptRequest(c) {
break
}
time.Sleep(1 * time.Second)
}
}
func interruptRequest(interrupted <-chan struct{}) bool {
select {
case <-interrupted:
fmt.Println("program is down....")
return true
default:
fmt.Println("program is running...")
}
return false
}
函数内对slice作append,在函数外不可见: slice的元信息是SliceHeader,slice按值传递和按引用传递其实就是将SliceHeader按值传递或按指针传递。
func main() {
s := make([]int, 0)
a := (*reflect.SliceHeader)(unsafe.Pointer(&s))
fmt.Printf("%+vn", a)
test(s)
b := (*reflect.SliceHeader)(unsafe.Pointer(&s))
fmt.Printf("%+vn", b)
}
func test(s []int) {
s = append(s, 1)
a := (*reflect.SliceHeader)(unsafe.Pointer(&s))
fmt.Printf("%+vn", a)
}
// output:
&{Data:18193560 Len:0 Cap:0}
&{Data:842350567624 Len:1 Cap:1}
&{Data:18193560 Len:0 Cap:0}
// -----------------------------------------------------
// 通过传slice指针,实现对slice的修改
func main() {
s := make([]int, 0)
a := (*reflect.SliceHeader)(unsafe.Pointer(&s))
fmt.Printf("%+vn", a)
test(&s)
b := (*reflect.SliceHeader)(unsafe.Pointer(&s))
fmt.Printf("%+vn", b)
}
func test(s *[]int) {
*s = append(*s, 1)
a := (*reflect.SliceHeader)(unsafe.Pointer(s))
fmt.Printf("%+vn", a)
}
// output:
&{Data:18193560 Len:0 Cap:0}
&{Data:842350567624 Len:1 Cap:1}
&{Data:842350567624 Len:1 Cap:1}
json自定义序列化
对于使用结构体中嵌套结构体的情况,只有receiver为指针类型,而嵌套结构体为结构体的值语义的时候不能触发自定义Json格式化函数MarshalJSON;其他三种组合均能够触发。
对于使用结构体中嵌套结构体slice的情况,receiver值语义、指针语义和嵌套结构体slice元素为值语义、指针语义的四种组合均能够触发Json格式化函数MarshalJSON。
package main
import (
"encoding/json"
"fmt"
)
type Profile struct {
Level string
Admin bool
}
// 本质是将Profile指针类型实现Marshaler接口,从而达到自定义json序列化格式的目的。
func (p *Profile) MarshalJSON() ([]byte, error) {
if p.Admin {
admin := struct {
Level string
}{
Level: "admin",
}
return json.Marshal(admin)
}
control := struct {
Level string
Admin bool
}{
Level: "control",
Admin: false,
}
return json.Marshal(control)
}
type User struct {
Id int
Name string
Age uint8
Profile *Profile
}
func main() {
u := User{
Id: 1,
Age: 23,
Name: "qshuai",
Profile: &Profile{
Level: "master",
Admin: true,
},
}
b, err := json.Marshal(u)
if err != nil {
panic(err)
}
fmt.Println(string(b))
}
// -----------------------------slice作为Struct成员的情况----------------------------
package main
import (
"encoding/json"
"fmt"
)
type Profile struct {
Level string
Admin bool
}
func (p *Profile) MarshalJSON() ([]byte, error) {
if p.Admin {
admin := struct {
Level string
}{
Level: "admin",
}
return json.Marshal(admin)
}
control := struct {
Level string
Admin bool
}{
Level: "control",
Admin: false,
}
return json.Marshal(control)
}
type User struct {
Id int
Name string
Age uint8
Profile []Profile
}
func main() {
u := User{
Id: 1,
Age: 23,
Name: "qshuai",
Profile: []Profile{
{
Level: "master",
Admin: true,
},
},
}
b, err := json.Marshal(u)
if err != nil {
panic(err)
}
fmt.Println(string(b))
}
使用map[string]interface{}接收不确定json数据
data := []byte(`
{
"address": "16WtgAckGAYLHaxJnRFV5mueF8gaEbKc4W",
"received": {"name":"qshuai", "age":23},
"sent": ["abc", 124, "def"]
}`)
var d map[string]interface{}
err := json.NewDecoder(bytes.NewReader(data)).Decode(&d)
if err != nil {
panic(err)
}
spew.Dump(d)
// output
(map[string]interface {}) (len=3) {
(string) (len=7) "address": (string) (len=34) "16WtgAckGAYLHaxJnRFV5mueF8gaEbKc4W",
(string) (len=8) "received": (map[string]interface {}) (len=2) {
(string) (len=4) "name": (string) (len=6) "qshuai",
(string) (len=3) "age": (float64) 23
},
(string) (len=4) "sent": ([]interface {}) (len=3 cap=4) {
(string) (len=3) "abc",
(float64) 124,
(string) (len=3) "def"
}
}
不可寻址的情况
常量
map的值且值为值类型
函数返回值并且返回值为值类型
接口断言且断言成值类型成功
函数返回值为数组类型(非数组指针类型),对返回值直接做[m:n]取slice的操作是不合法的
赋值简写方式的坑
// 1、同一作用域 := 左侧至少有一个未申明
// 2、同一作用域 := 左侧重复的变量名的变量地址是相同的
package main
import "fmt"
func main() {
a, b := 1, 2
fmt.Printf("%pn", &a)
a, c := 2, 3
fmt.Printf("%pn", &a)
fmt.Println(b, c)
}
for…range 坑
package main
import "fmt"
type T struct {
ID int
}
func (t *T) PrintID() {
fmt.Println(t.ID)
}
func F1() {
ts := []T{{1}, {2}, {3}}
for _, t := range ts { // 每次遍历,将ts中的相应的元素拷贝到临时变量t,同一个迭代的t的内存地址是相同的
defer t.PrintID() // 先取出t的地址,然后压栈
}
}
func F2() {
ts := []*T{&T{1}, &T{2}, &T{3}}
for _, t := range ts {
defer t.PrintID()
}
}
func main() {
fmt.Println("F1()")
F1()
fmt.Println()
fmt.Println("F2()")
F2()
}
== nil
的判断结果: 如果左侧为接口类型,那么只有接口变量的值和类型均为nil的情况下,== nil
才能成立;如果左侧是普通数据类型,那么只要其变量的值为nil,那么== nil
就会成立。
以Example_
开头的函数会在go test
时自动运行。
golang中的引用类型: slice、map、channel、function、interface、pointer
nil指针可以调用自己的方法,但是不能在函数内对指针进行解引用或者访问其成员变量
package main
import "fmt"
type user struct {
ID int
Name string
}
func (u *user) getInfo() {
fmt.Println("hello world")
// fmt.Println(u.Name) // will panic
}
func main() {
var u *user
if u == nil {
fmt.Println("u == nil")
}
u.getInfo() // ok
}
nil slice除了不能使用索引访问外,其他操作都是可以的
package main
import "fmt"
func main() {
var s []int
s[0] = 1 // panic
fmt.Println(s[1]) // panic
fmt.Println(len(s), cap(s)) // 0 0
for index, item := range s { // nothing
fmt.Println(index, item)
}
s = append(s, 1)
fmt.Println(s) // [1]
}
nil map除了不能写入元素外,其他操作都是允许的(可以把nil map看成只读的map)
package main
import "fmt"
func main() {
var m map[int]string
fmt.Println(m == nil) // true
fmt.Println(len(m)) // 0
for key, value := range m { // nothing
fmt.Println(key, value)
}
item, ok := m[2]
fmt.Println(item, ok) // false
m[0] = "qshuai" // panic
m1 := map[int]string{}
fmt.Println(m1 == nil) // false
m1[1] = "qshuai"
fmt.Println(m1[1]) // qshuai
}
nil channel的写入和读取都会造成死锁,而关闭一个nil channel会引起panic
func main() {
var c chan int
fmt.Println(c == nil) // true
<-c // deadlock
c <- 3 // deadlock
close(c) // panic
}
已关闭的channle仍然可以被读取,只不过结果总为对应类型的零值
func main() {
cc := make(chan int)
go func() {
stop:
for {
select {
case v, ok := <-cc:
if ok {
fmt.Println(v)
} else {
break stop // will not into for loop
}
}
}
}()
go func() {
var i int
for {
cc <- i
i++
time.Sleep(500 * time.Millisecond)
if i > 5 {
close(cc)
break
}
}
}()
time.Sleep(10 * time.Second)
}
switch中
的fallthrough
用法: fallthrough
将执行下一个case
内的语句, 而不用判断case
的条件是否满足。
func main() {
i := 3
switch i {
default:
fallthrough
case 1:
fmt.Println("ok")
case 2:
fmt.Println("false")
}
}
// output: ok
switch case的高级用法
func isAcceptableKind(kind reflect.Kind) bool {
switch kind {
case reflect.Chan:
fallthrough
case reflect.Complex64:
fallthrough
case reflect.Complex128:
fallthrough
case reflect.Func:
fallthrough
case reflect.Ptr:
fallthrough
case reflect.Interface:
return false
}
return true
}
slice用法
data[:6:8] 每个数字前都有个冒号, slice内容为data从0到第6位,长度len为6,最大扩充项cap设置为8
检查关闭通道
w.quitMu.Lock()
// select 中只有一个通道操作,之所有用select是因为,select可以通过default的方式避开
// 堵塞的情况
select {
case <-w.quit:
w.quitMu.Unlock()
return
default:
}
如果觉得我的文章对您有用,请随意打赏。你的支持将鼓励我继续创作!