4.说说Go语言中,数组和切片的区别?切片的底层
数组 数组的长度是类型的一部分,不同的长度的数组,类型不是一致的
a := [3]int{1, 2, 3}
b := [4]int{1, 2, 3}
fmt.Printf("a 的类型为%s, b的类型为%s\n", reflect.TypeOf(a), reflect.TypeOf(b))
fmt.Println(reflect.TypeOf(a) == reflect.TypeOf(b))
// a 的类型为[3]int, b的类型为[4]int
// false
as := a[0:2]
bs := b[0:2]
fmt.Printf("as的类型为%v,bs的类型为%v\n", reflect.TypeOf(as), reflect.TypeOf(bs))
fmt.Println(reflect.TypeOf(as) == reflect.TypeOf(bs))
// as的类型为[]int,bs的类型为[]int
// true
func main(){
arrayA := [2]int{100, 200}
var arrayB [2]int
arrayB = arrayA
fmt.Printf("arrayA : %v , %v\n", &arrayA[0], arrayA)
fmt.Printf("arrayB : %v , %v\n", &arrayB[0], arrayB)
testArray(arrayA)
fmt.Println()
sliceA := arrayA[0:1]
sliceB := sliceA
fmt.Printf("sliceA : %v , %v\n", &sliceA[0], sliceA)
fmt.Printf("sliceB : %v , %v\n", &sliceB[0], sliceB)
testSlice(sliceA)
}
func testArray(x [2]int) {
fmt.Printf("func Array : %v , %v\n", &x[0], x)
}
func testSlice(x []int) {
fmt.Printf("func Slice : %v , %v\n", &x[0], x)
}
/*
arrayA : 0xc0000180b0 , [100 200]
arrayB : 0xc0000180c0 , [100 200]
func Array : 0xc000018110 , [100 200]
sliceA : 0xc0000180b0 , [100]
sliceB : 0xc0000180b0 , [100]
func Slice : 0xc0000180b0 , [100]
*/
c := [...]int{1,2,3,4}
fmt.Printf("c 的类型为%s, 长度为%v\n", reflect.TypeOf(c), len(c))
// c 的类型为[4]int, 长度为4
cs := []int{1, 2, 3, 4}
cs = append(cs, 5)
fmt.Printf("cs 的类型为%s, 长度为%v\n", reflect.TypeOf(cs), len(cs))
// cs 的类型为[]int, 长度为5
type slice struct {
array unsafe.Pointer // 指向数组的指针
len int // 当前切片的长度
cap int // 当前切片容量
}
func main() {
arr := [4]int{1, 2, 3, 4}
sliceA := arr[:]
fmt.Printf("len(silceA) = %v, cap(sliceA) = %v\n", len(sliceA), cap(sliceA))
// len(silceA) = 4, cap(sliceA) = 4
// 修改arr的第一个元素值,sliceA的第一个元素值跟着变化
arr[0] = 5
fmt.Printf("sliceA = %v\n", sliceA)
// sliceA = [5 2 3 4]
// 给slice添加元素,导致slice扩容,空间重新分配
sliceA = append(sliceA, 6)
fmt.Printf("sliceA = %v\n", sliceA)
fmt.Printf("len(silceA) = %v, cap(sliceA) = %v\n", len(sliceA), cap(sliceA))
// sliceA = [5 2 3 4 6]
// len(silceA) = 5, cap(sliceA) = 8
// 修改arr的第一个元素值,发现sliceA的第一个元素值不在跟着变化
arr[0] = 7
fmt.Printf("arr = %v\n", arr)
fmt.Printf("sliceA = %v\n", sliceA)
// arr = [7 2 3 4]
// sliceA = [5 2 3 4 6]
}
func main() {
arr := [3]int{1, 2, 3}
slice := arr[0:1]
slice2 := append(slice, 5, 6, 7)
fmt.Println(arr, slice, slice2)
}
// [1 2 3] [1] [1 5 6 7]
func main() {
arr := [3]int{1, 2, 3}
slice := arr[0:1]
slice2 := append(slice, 5, 6)
fmt.Println(arr, slice, slice2)
}
// [1,5,6] [1] [1,5,6]
5. 说说go语⾔的同步锁
var x = 0
var waitGroup sync.WaitGroup
func main() {
waitGroup.Add(2)
go add()
go add()
waitGroup.Wait()
fmt.Println(x)
}
func add() {
for i := 0; i < 10000; i++ {
y := x
y = y + 1
x = y
}
waitGroup.Done()
}
var x = 0
var waitGroup sync.WaitGroup
var mutex sync.Mutex
func main() {
waitGroup.Add(2)
go add()
go add()
waitGroup.Wait()
fmt.Println(x)
}
func add() {
for i := 0; i < 10000; i++ {
mutex.Lock()
y := x
y = y + 1
x = y
mutex.UnLock()
}
waitGroup.Done()
}
于此之外还有读写锁,即
var rwmutex sync.RWMutex
rwmutex.Lock()// 加写锁,阻止其他读写获取锁
rwmutex.RLock()// 加读锁,如果其他gorountine要获取读锁的话,会获得读锁,如果要获取写锁就会等待
channel特性
给⼀个 nil channel 发送数据,造成永远阻塞
从⼀个 nil channel 接收数据,造成永远阻塞
给⼀个已经关闭的 channel 发送数据,引起 panic
从⼀个已经关闭的 channel 接收数据,如果缓冲区中为空,则返回⼀个零值
⽆缓冲的channel是同步的,⽽有缓冲的channel是⾮同步的
func main() {
c := make(chan int)
go add(c)
go send(c)
// 给5秒时间让前两个goroutine有足够时间运行
time.Sleep(5 * time.Second)
}
// 不断向channel c中发送[0,10)的随机数
func send(c chan int) {
for {
c <- rand.Intn(10)
}
}
func add(c chan int) {
sum := 0
// 1秒后,将向t.C通道发送时间点,使其可读
t := time.NewTimer(2 * time.Second)
for {
// 两秒内,将一直选择第一个case
// 两秒后,t.C可读,将选择第二个case
// c变成nil channel后,两个case分支都将一直阻塞
select {
case input := <-c:
// 不断读取c中的随机数据进行加总
sum = sum + input
case <-t.C:
c = nil
fmt.Println(sum)
}
}
}
func main() {
c := make(chan int)
go send(c)
go receive(c)
// 给5秒时间让前两个goroutine有足够时间运行
time.Sleep(5 * time.Second)
}
// 不断向channel c中发送[0,10)的随机数
func send(c chan int) {
for {
c <- rand.Intn(10)
}
}
func receive(c chan int) {
i := 0
t := time.NewTimer(1 * time.Second)
for {
select {
case <-t.C:
c = nil
case <-c:
fmt.Println(i)
i++
}
}
}