golang面试题(带答案)[通俗易懂]

golang面试题(带答案)[通俗易懂]1.下面代码输出什么,为什么 //make([]T,length,capacity) s1:=[]int{1,2,3} fmt.Println(s1,”哈哈”)//[123] s2:=s1 fmt.Println(s1,”哈哈”)//[123] fori:=0;i<3;i++{ s2[i]=s2[i]+1 } fmt.Println(s1)//[234] fmt.Println(s2)//[234][12

大家好,又见面了,我是你们的朋友全栈君。

1.下面代码输出什么,为什么

	//make([]T, length, capacity)
	s1 := []int{ 
   1, 2, 3}
	fmt.Println(s1, "哈哈") //[1 2 3]

	s2 := s1
	fmt.Println(s1, "哈哈") //[1 2 3]
	for i := 0; i < 3; i++ { 
   
		s2[i] = s2[i] + 1
	}
	fmt.Println(s1) //[2 3 4]
	fmt.Println(s2) //[2 3 4]
[1 2 3] 哈哈
[1 2 3] 哈哈
[2 3 4]
[2 3 4]

注:引用就是同一份,相当于起了一个别名,就是多起了一个名字而已。
在Go语言中的引用类型有:映射(map),数组切片(slice),通道(channel),方法与函数。
整型,字符串,布尔,数组在当作参数传递时,是传递副本的内存地址,也就是值传递。
2.下面代码输出什么,为什么

func rmLast(a []int) { 
   
	fmt.Println("a", a)
	a = a[:len(a)-1]
	for i := 0; i < 13; i++ { 
   
		a = append(a, 8)
	}

	fmt.Println("rm后a", a)
	// fmt.Println(len(a))
}
func updateLast(a []int) { 
   
	fmt.Println("a", a)
	for i := 0; i < len(a); i++ { 
   
		a[i] = a[i] + 1
	}
	fmt.Println("修改后a", a)
	// fmt.Println(len(a))
}
func main() { 
   
	xyz := []int{ 
   1, 2, 3, 4, 5, 6, 7, 8, 9}
	fmt.Println("xyz", xyz)
	rmLast(xyz)
	fmt.Println("rm后xyz", xyz)
	updateLast(xyz)
	fmt.Println("修改后xyz", xyz) //[1 2 3 4 5 6 7 8 9]
}

xyz [1 2 3 4 5 6 7 8 9]
a [1 2 3 4 5 6 7 8 9]
rm后a [1 2 3 4 5 6 7 8 8 8 8 8 8 8 8 8 8 8 8 8 8]
rm后xyz [1 2 3 4 5 6 7 8 8]
a [1 2 3 4 5 6 7 8 8]
修改后a [2 3 4 5 6 7 8 9 9]
修改后xyz [2 3 4 5 6 7 8 9 9]

注:函数内改切片的值,外部是可见的,改切片的长度外部是不可见的
3.下面代码输出什么,为什么

	//n1是n2的底层数组
	n1 := [3]int{ 
   1, 2, 3}
	n2 := n1[0:3]
	fmt.Println("下面是n1地址 ")
	for i := 0; i < len(n1); i++ { 
   
		fmt.Printf("%p\n", &n1[i])

	}
	fmt.Println(n1)

	fmt.Println("下面是n2地址 ")
	for i := 0; i < len(n2); i++ { 
   
		fmt.Printf("%p\n", &n2[i])

	}
	fmt.Println(n2)

	n2 = append(n2, 1)
	fmt.Println("下面是n1地址 ")
	for i := 0; i < len(n1); i++ { 
   
		fmt.Printf("%p\n", &n1[i])

	}
	fmt.Println(n1)

	fmt.Println("下面是n2地址 ")
	for i := 0; i < len(n2); i++ { 
   
		fmt.Printf("%p\n", &n2[i])
	}
	fmt.Println(n2)
下面是n1地址 
0xc000064140  
0xc000064148  
0xc000064150  
[1 2 3]       
下面是n2地址  
0xc000064140  
0xc000064148
0xc000064150
[1 2 3]
下面是n1地址
0xc000064140
0xc000064148
0xc000064150
[1 2 3]
下面是n2地址
0xc000090030
0xc000090038
0xc000090040
0xc000090048
[1 2 3 1]

4.下面代码输出什么,为什么

func defer_call(y int) { 
   
	for i := 0; i < 5; i++ { 
   
		defer fmt.Println("输出y+i", y+i)
		fmt.Println("哈哈")
		defer fmt.Println("输出i ", i)
	}

}
func main() { 
   
	defer_call(5)
}
哈哈
哈哈
哈哈
哈哈
哈哈
输出i  4
输出y+1 9
输出i  3
输出y+1 8
输出i  2
输出y+1 7
输出i  1
输出y+1 6
输出i  0
输出y+1 5
注:先执行"哈哈",
退出defer_call这个函数时,反序执行以下函数
fmt.Println("输出y+i",5)
fmt.Println("i",0)
fmt.Println("输出y+i",6)
fmt.Println(....)
fmt.Println("输出y+i",9)
fmt.Println("i",4)

5.下面代码输出什么

func main() { 
   
	for i := 0; i < 5; i++ { 
   
		fmt.Println(i, "haha")
		//匿名函数:匿名函数可以在声明后调用
		go func() { 
   
			time.Sleep(3 * time.Second)

			fmt.Println(i, "嗯嗯")
		}()
	}

	time.Sleep(10 * time.Second)

}

0 haha
1 haha
2 haha
3 haha
4 haha
5 嗯嗯
5 嗯嗯
5 嗯嗯
5 嗯嗯
5 嗯嗯

6.下面代码输出什么

func main() { 
   
	strs := []string{ 
   "one", "two", "three"}
	for _, s := range strs { 
   
	//1.主线程都快结束了(主线程跑到three了),并发出来的线程才刚开始
	//2.这里匿名函数不传参的话,是共享的s的地址,所以打印出来的都是three
		go func() { 
   

			time.Sleep(1 * time.Second)

			fmt.Printf("%s ", s)
		}()
	}
	time.Sleep(3 * time.Second)
}
three three three

7.下面代码输出什么

func main() { 
   
	strs := []string{ 
   "one", "two", "three"}
	for _, s := range strs { 
   
		go func(s string) { 
   

			time.Sleep(1 * time.Second)

			fmt.Printf("%s ", s)
		}(s)
	}
	time.Sleep(3 * time.Second)
}
three one two
one two three
one three two

注:并发出来三个线程,哪个先结束不一定,所以输出不固定
8.下面代码输出什么

func main() { 
   

	x := []string{ 
   "ha", "b", "c"}

	for v := range x { 
   

		fmt.Print(v)

	}

}
012

9.下面代码输出什么

type Slice []int

func NewSlice() Slice { 
   

	return make(Slice, 0)

}

func (s *Slice) Add(elem int) *Slice { 
   

	*s = append(*s, elem)

	fmt.Print(elem)

	return s

}

func main() { 
   

	s := NewSlice()

	defer s.Add(1).Add(2)

	s.Add(3)

}
132

注:一个defer只能延迟调用一个函数,defer s.Add(1).Add(2),先执行参数部分,然后再延迟调用Add(2)
10.下面代码输出什么

package main

import (
	"fmt"
)

func main() { 
   
	defer_call()
}
func defer_call() { 
   
	defer func() { 
    fmt.Println("打印前") }()
	defer func() { 
    fmt.Println("打印中") }()
	defer func() { 
    fmt.Println("打印后") }()
	panic("触发异常")
}

考点:defer执行顺序
解答:
defer 是后进先出。
panic 需要等defer 结束后才会向上传递。 出现panic恐慌时候,会先按照defer的后入先出的顺序执行,最后才会执行panic。

打印后
打印中
打印前
panic: 触发异常
11.下面代码输出什么

package main

import (
	"fmt"
)

// 遍历切片的每个元素, 通过给定函数进行元素访问
func visit(list []int, f func(int)) { 
   
	for _, v := range list { 
   
		f(v)
	}
}
func main() { 
   
	// 使用匿名函数打印切片内容
	visit([]int{ 
   1, 2, 3, 4}, func(v int) { 
   
		fmt.Println(v)
	})
}

1
2
3
4
12.下面代码输出什么

package main

import (
	"fmt"
	"runtime"
	"sync"
)

func main() { 
   
	//GOMAXPROCS设置可同时执行的最大CPU数,并返回先前的设置。
	// 若 n < 1,它就不会更改当前设置。本地机器的逻辑CPU数可通过 NumCPU 查询。
	//本函数在调度程序优化后会去掉。
	runtime.GOMAXPROCS(1)
	wg := sync.WaitGroup{ 
   }
	wg.Add(20)
	for i := 0; i < 10; i++ { 
    //主线程
		go func() { 
    //并发出来的线程
			fmt.Println("A: ", i)
			wg.Done()
		}()
	}
	for i := 0; i < 10; i++ { 
   
		go func(i int) { 
   
			fmt.Println("B: ", i)
			wg.Done()
		}(i)
	}
	wg.Wait()
}

考点:go执行的随机性和闭包
解答:
谁也不知道执行后打印的顺序是什么样的,所以只能说是随机数字。 但是A:均为输出10,B:从0~9输出(顺序不定)。 第一个go func中i是外部for的一个变量,地址不变化。遍历完成后,最终i=10。 故go func执行时,i的值始终是10。

第二个go func中i是函数参数,与外部for中的i完全是两个变量。 尾部(i)将发生值拷贝,go func内部指向值拷贝地址。
13.下面代码输出什么

package main

import (
	"fmt"
	"runtime"
	"sync"
)

func main() { 
   
	//GOMAXPROCS设置可同时执行的最大CPU数,并返回先前的设置。
	// 若 n < 1,它就不会更改当前设置。本地机器的逻辑CPU数可通过 NumCPU 查询。
	//本函数在调度程序优化后会去掉。
	runtime.GOMAXPROCS(2)
	wg := sync.WaitGroup{ 
   }
	wg.Add(3)
	for i := 0; i < 10; i++ { 
    //主线程
		go func() { 
    //并发出来的线程
			fmt.Println("A: ", i)
			wg.Done()
		}()

	}
	wg.Wait()
	wg.Add(7)
	wgg := sync.WaitGroup{ 
   }
	wgg.Add(5)
	for i := 0; i < 10; i++ { 
   
		go func(i int) { 
   
			fmt.Println("B: ", i)
			wgg.Done()

		}(i)
	}
	wgg.Wait()

}

14.下面代码会触发异常吗?请详细说明



package main

import (
	"fmt"
	"runtime"
)

func main() { 
   
    runtime.GOMAXPROCS(1)
    int_chan := make(chan int, 1)
    string_chan := make(chan string, 1)
    int_chan <-1
    string_chan <-"hello"
    select { 
      
         case value := <-int_chan:
       fmt.Println(value)
          case value := <-string_chan:        
          panic(value)
    }
}

考点:select随机性
解答:
select会随机选择一个可用通用做收发操作。 所以代码是有肯触发异常,也有可能不会。 单个chan如果无缓冲时,将会阻塞。但结合 select可以在多个chan间等待执行。有三点原则:

select 中只要有一个case能return,则立刻执行。
当如果同一时间有多个case均能return则伪随机方式抽取任意一个执行。
如果没有一个case能return则可以执行”default”块。
15.下面代码有什么问题,如何修改

package main

import (
	"fmt"
	"sync"
)

type UserAges struct { 
   
	ages map[string]int
	sync.Mutex
}

func (ua *UserAges) Add(name string, age int) { 
   
	ua.Lock()
	defer ua.Unlock()
	ua.ages[name] = age
}
func (ua *UserAges) Get(name string) int { 
   
	if age, ok := ua.ages[name]; ok { 
   
		return age
	}
	return -1
}

func main() { 
   
	var userAges UserAges
	userAges.Add("lisa", 24)
	userAges.Get("lisa")
	fmt.Println(userAges.ages)
}

panic: assignment to entry in nil map

goroutine 1 [running]:
main.(*UserAges).Add(0xc0000521c0, 0x4c492f, 0x4, 0x18)
C:/Users/zyq/go/src/yj/ra.go:17 +0x98
main.main()
C:/Users/zyq/go/src/yj/ra.go:28 +0x69
exit status 2
修改后:

func (ua *UserAges) Add(name string, age int) { 
   
	ua.ages = make(map[string]int)
	ua.Lock()
	defer ua.Unlock()
	ua.ages[name] = age
}

输出结果:

map[lisa:24]

16.下面代码输出什么,为什么

package main

import (
	"fmt"
	"sync"
)

type threadSafeSet struct { 
   
	sync.RWMutex
	s []interface{ 
   }
}

func (set *threadSafeSet) Iter() <-chan interface{ 
   } { 
   
	// ch := make(chan interface{}) // 解除注释看看!
	ch := make(chan interface{ 
   }, len(set.s))
	go func() { 
   
		set.RLock()
		for _, elem := range set.s { 
   
			ch <- elem
			fmt.Println("Iter:", elem)
		}
		close(ch)
		set.RUnlock()
	}()
	return ch
}
func main() { 
   
	th := threadSafeSet{ 
   
		s: []interface{ 
   }{ 
   "1", "2"},
	}
	v := <-th.Iter()
	fmt.Println(v)

}

Iter: 1
Iter: 2
1
解答:ch := make(chan interface{}, len(set.s)) 每次都会分配一个新的channel,v := <-th.Iter()只传了一个值,所以v是数组s的第一个value
17.以下代码输出什么?为什么?

package main

import (
	"fmt"
)

type People interface { 
   
	Show()
}
type Student struct{ 
   }

func (stu *Student) Show() { 
   
}
func live() People { 
   
	var stu *Student
	return stu
}
func main() { 
   
	if live() == nil { 
   
		fmt.Println("AAAAAAA")
	} else { 
   
		fmt.Println("BBBBBBB")
	}
}

输出:
BBBBBBB

18.以下代码输出什么?为什么?

package main

func main() { 
   
	i := GetValue()
	switch i.(type) { 
   
	case int:
		println("int")
	case string:
		println("string")
	case interface{ 
   }:
		println("interface")
	default:
		println("unknown")
	}
}
func GetValue() int { 
   
	return 1
}

解析
考点:type

编译失败,因为type只能使用在interface
修改后:

package main

func main() { 
   
	i := GetValue()
	switch i.(type) { 
   
	case int:
		println("int")
	case string:
		println("string")
	case interface{ 
   }:
		println("interface")
	default:
		println("unknown")
	}
}
func GetValue() interface{ 
   } { 
   
	return 1
}

19.以下代码输出什么?为什么?

package main

import "fmt"

func main() { 
   
	s1 := []int{ 
   1, 2, 3}
	s2 := []int{ 
   4, 5}
	s1 = append(s1, s2...)
	fmt.Println(s1)
	fmt.Println(s2)
	var a = []int{ 
   1, 2, 3}
	a = append([]int{ 
   0}, a...)
	fmt.Println(a)
	a = append([]int{ 
   -3, -2, -1}, a...)
	fmt.Println(a)

}

[1 2 3 4 5]
[4 5]
[0 1 2 3]
[-3 -2 -1 0 1 2 3]
考点:
1.追加一个切片, 切片需要解包s1 = append(s1, s2…)
2.除了在切片的尾部追加,我们还可以在切片的开头添加元素
20.以下代码输出什么?为什么?

package main

import "fmt"

func GetValue(m map[int]string, id int) (string, bool) { 
   
	if _, exist := m[id]; exist { 
   
		return "存在数据", true
	}
	return "", false
}
func main() { 
   
	intmap := map[int]string{ 
   
		1: "a",
		2: "bb",
		3: "ccc",
	}
	v, err := GetValue(intmap, 3)
	fmt.Println(v, err)
}

存在数据 true
考点:map的key是否存在
21.以下代码输出什么?为什么?

package main

func DeferFunc1(i int) (t int) { 
   
	t = i
	defer func() { 
   
		t += 3
	}()
	return t
}
func DeferFunc2(i int) int { 
   
	t := i
	defer func() { 
   
		t += 3
	}()
	return t
}
func DeferFunc3(i int) (t int) { 
   
	defer func() { 
   
		t += i
	}()
	return 2
}
func main() { 
   
	println(DeferFunc1(1))
	println(DeferFunc2(1))
	println(DeferFunc3(1))
}

4
1
3
考点:
这个题的重点在于返回值有没有名字,有名字的话,返回值受defer里的func的影响,没有名字的话不受影响
22.以下代码输出什么?为什么?

package main

import "fmt"

const (
	x = iota
	y
	z = "zz"
	k
	p = iota
	c
	d = "111"
	f
)

func main() { 
   
	fmt.Println(x, y, z, k, p, c, d, f)
}

0 1 zz zz 4 5 111 111
考点:
在常量声明中,预声明的标识符 iota表示连续的无类型整数 常量。它的值是该 常量声明中相应ConstSpec的索引,从零开始。它可以用来构造一组相关的常量:

const(
c0 = iota // c0 == 0
c1 = iota // c1 == 1
c2 = iota // c2 == 2

const(
a = 1 << iota // a == 1(iota == 0)
b = 1 << iota // b == 2(iota == 1)
c = 3 // c == 3(iota == 2,未使用)
d = 1 << iota // d == 8(iota == 3)

const(
u = iota * 42 // u == 0(无类型整数常量)
v float64 = iota * 42 // v == 42.0(float64常数)
w = iota * 42 // w == 84(无类型整数常量)

23.以下代码输出什么?为什么

package main

var size = 1024
var max_size = size * 2

func main() { 
   
	println(size, max_size)
}

extra expression in var declaration
考点:变量简短模式
变量简短模式限制:
定义变量同时显式初始化
不能提供数据类型
只能在函数内部使用

var a int
var a = 1
a:=1

24.以下代码能输出吗?为什么

package main
import"fmt"
func main() { 
       
 type MyInt1 int    
 type MyInt2 = int
    var i int =9
    var i1 MyInt1 = i
    var i2 MyInt2 = i
    fmt.Println(i1,i2)
}

cannot use i (type int) as type MyInt1 in assignment
考点:
基于一个类型创建一个新类型,称之为defintion;基于一个类型创建一个别名,称之为alias。 MyInt1为称之为defintion,虽然底层类型为int类型,但是不能直接赋值,需要强转; MyInt2称之为alias,可以直接赋值。
25.以下代码能输出吗?为什么

package main

import "fmt"

type User struct { 
   
}
type MyUser1 User
type MyUser2 = User

func (i MyUser1) m1() { 
   
	fmt.Println("MyUser1.m1")
}
func (i User) m2() { 
   
	fmt.Println("User.m2")
}
func main() { 
   
	var i1 MyUser1
	var i2 MyUser2
	i1.m1()
	i2.m2()
	i1.m2()

}

输出:
MyUser1.m1
User.m2
i1.m2()是不能执行的,因为MyUser1没有定义该方法。
考点:
因为MyUser2完全等价于User,所以具有其所有的方法,并且其中一个新增了方法,另外一个也会有。
26.使用WaitGroup并发计算两组数据的和

package main

import (
	"fmt"
	"sync"
)

func main() { 
   
	wg := sync.WaitGroup{ 
   }
	var xValue int
	var iValue int
	wg.Add(2) //开两个进程
	go func() { 
   
		for x := 0; x <= 50; x++ { 
   
			xValue += x
		}
		fmt.Println("xValue: ", xValue)
		wg.Done() //放在进程里面最后,确认这个进程做完了计数器减一

	}()
	//wg.Done() 如果done放在这里的话,不能确认上边的进程做了还是没做,都会减一
	go func() { 
   
		for i := 51; i <= 100; i++ { 
   
			iValue += i
		}
		fmt.Println("iValue: ", iValue)
		wg.Done()
	}()
	wg.Wait()
	fmt.Println("sumValue", xValue+iValue)

}

iValue: 3775
xValue: 1275
sumValue 5050
27.使用通道并发计算两组数据的和

package main

import (
	"fmt"
)

func main() { 
   
	c := make(chan int, 2)
	go func() { 
   
		var xValue int
		for x := 0; x <= 50; x++ { 
   
			xValue += x
		}
		c <- xValue

	}()
	go func() { 
   
		var iValue int
		for i := 51; i <= 100; i++ { 
   
			iValue += i
		}
		c <- iValue

	}()
	x, y := <-c, <-c
	fmt.Println("sumValue", x+y)

}

sumValue 5050
28.下面代码输出什么,为什么

package main

func main() { 
   
	done := make(chan struct{ 
   }) //结束事件
	c := make(chan string)      //数据传输通道
	go func() { 
   
		s := <-c //接受消息
		println(s)
		close(done) //关闭通道,作为结束通知
	}()
	c <- "hi!" //发送消息
	<-done     //阻塞,直到所有数据或管道关闭
}

hi!
29.下面代码输出什么,为什么

package main

import (
	"sync"
	"time"
)

func main() { 
   
	var wg sync.WaitGroup
	ready := make(chan struct{ 
   })
	for i := 0; i < 3; i++ { 
   
		wg.Add(1)
		go func(id int) { 
   
			defer wg.Done()
			println(id, ":ready.")
			<-ready //因为取不到,所以阻塞
			println(id, ": running...")
		}(i)
	}
	time.Sleep(time.Second)
	println("ready?Go!")
	close(ready) //close之后,关闭通道解除阻塞
	wg.Wait()

}

0 :ready.
1 :ready.
2 :ready.
ready?Go!
2 : running…
1 : running…
0 : running…
30.下面代码输出什么,为什么

package main

func main() { 
   
	done := make(chan struct{ 
   }) //用于结束事件
	c := make(chan int)         //数据传输通道
	go func() { 
   
		defer close(done)  //通知关闭
		for x := range c { 
    //循环读取消息直到通道被关闭
			println(x)
		}
	}()
	c <- 1
	c <- 2
	c <- 3
	close(c) //通道关闭解除阻塞
	<-done   //阻塞,直到有数据或通知关闭

}

1
2
3
31.下面代码输出什么,为什么

package main

func main() { 
   
	done := make(chan struct{ 
   }) //用于结束事件
	c := make(chan int)         //数据传输通道
	go func() { 
   
		defer close(done) //通知关闭
		for { 
   
			x, ok := <-c
			if !ok { 
    //据此判断通道是否被关闭
				return
			}
			println(x)
		} //通道关闭,循环结束
	}()
	c <- 1
	c <- 2
	c <- 3
	close(c) //通道关闭解除阻塞
	<-done   //阻塞,直到有数据或通知关闭

}

1
2
3
32.下面代码输出什么,为什么

package main

func main() { 
   
	c := make(chan int, 3) //数据传输通道
	c <- 10
	c <- 20
	close(c)
	for i := 0; i < cap(c)+1; i++ { 
   
		x, ok := <-c
		println(i, ":", ok, x)
	}

}

0 : true 10
1 : true 20
2 : false 0
3 : false 0
33.下面代码输出什么,为什么
如果同时处理多个通道,可选用select语句,他会随机选用一个可用通道做收发操作

package main

import "sync"

func main() { 
   
	var wg sync.WaitGroup
	wg.Add(2)
	a, b := make(chan int), make(chan int)
	go func() { 
    //接收端
		defer wg.Done()
		for { 
   
			var (
				name string
				x    int
				ok   bool
			)
			select { 
    //随机选择可用channel接收数据
			case x, ok = <-a:
				name = "a"
			case x, ok = <-b:
				name = "b"
			}
			if !ok { 
   
				return //如果任一通道关闭,则终止接收
			}
			println(name, x) //输出接收的数据信息
		}
	}()
	go func() { 
    //发送端
		defer wg.Done()
		defer close(a)
		defer close(b)
		for i := 0; i < 10; i++ { 
   
			select { 
    //随机选择发送的channel
			case a <- i:
			case b <- i * 10:
			}
		}
	}()
	wg.Wait()
}

输出一
C:\Users\zyq\go\src\LearnGoLang\learn>go run hello.go
b 0
a 1
a 2
a 3
b 40
b 50
b 60
a 7
a 8
a 9
输出二
C:\Users\zyq\go\src\LearnGoLang\learn>go run hello.go
a 0
b 10
b 20
b 30
a 4
a 5
b 60
b 70
b 80
a 9
输出…
34.下面代码输出什么,为什么
通道默认是双向的,并不区分发送和接收端,但某些时候我们可以限制收发操作的方向来获得更严谨的操作逻辑。
通常使用类型转换来获取单向通道,并分别赋予操作双方。

package main

import "sync"

func main() { 
   
	var wg sync.WaitGroup
	wg.Add(2)
	c := make(chan int)
	var send chan<- int = c //仅发送
	var recv <-chan int = c //仅接收
	go func() { 
                //接收端
		defer wg.Done()
		for x := range recv { 
   
			println(x)
		}
	}()
	go func() { 
    //发送端
		defer wg.Done()
		defer close(c)
		for i := 0; i < 3; i++ { 
   
			send <- i
		}
	}()
	wg.Wait()
}

0
1
2
注意:
1.不能在单向通上做逆向操作
2.close()不能用于接收端
3.无法将单向通道重新转换回去
35.下面代码输出什么,为什么
问题:通道能传slice吗

package main

import (
	"fmt"
	"sync"
)

func main() { 
   
	var wg sync.WaitGroup
	wg.Add(2)
	a, b := make(chan []int), make(chan []int)
	go func() { 
    //接收端
		defer wg.Done()
		for { 
   
			var (
				name string
				x    []int
				ok   bool
			)
			select { 
    //随机选择可用channel接收数据
			case x, ok = <-a:
				name = "a"
			case x, ok = <-b:
				name = "b"
			}
			if !ok { 
   
				return //如果任一通道关闭,则终止接收
			}
			x[0] = 99
			fmt.Println(name, x) //输出接收的数据信息
		}
	}()

	i := []int{ 
   1, 2, 3, 4}
	go func() { 
    //发送端
		defer wg.Done()
		defer close(a)
		defer close(b)
		fmt.Println(i)
		select { 
    //随机选择发送的channel
		case a <- i:
		}

	}()
	wg.Wait()
	fmt.Println(i)
}

[1 2 3 4]
a [99 2 3 4]
[99 2 3 4]
36.第33题需要怎么改满足,等待全部通道消息处理结束,而不是任一通道关闭就终止接收
提示:如要等全部通道消息处理结束,可将已完成通道设置为nil,这样它就会被阻塞,不再被select选中。

package main

import "sync"

func main() { 
   
	var wg sync.WaitGroup
	wg.Add(3)
	a, b := make(chan int), make(chan int)
	go func() { 
    //接收端
		defer wg.Done()
		for { 
   
			select { 
    //随机选择可用channel接收数据
			case x, ok := <-a:
				if !ok { 
   
					a = nil
					break
					//用于循环语句中跳出循环,并开始执行循环之后的语句。
					//break 在 switch(开关语句)中在执行一条 case 后跳出语句的作用。
				}
				println("a", x)
			case x, ok := <-b:
				if !ok { 
   
					b = nil
					break
				}
				println("b", x)
			}
			if a == nil && b == nil { 
    //全部结束,退出循环
				return
			}
		}
	}()
	go func() { 
    //发送端a
		defer wg.Done()
		defer close(a)
		for i := 0; i < 3; i++ { 
   

			a <- i

		}
	}()
	go func() { 
    //发送端b
		defer wg.Done()
		defer close(b)
		for i := 0; i < 3; i++ { 
   

			b <- i * 10

		}
	}()
	wg.Wait()
}

a 0
b 0
b 10
a 1
a 2
b 20
37.下面代码输出什么,为什么
如果是同一通道,也会随机选择case执行

package main

import "sync"

func main() { 
   
	var wg sync.WaitGroup
	wg.Add(2)
	c := make(chan int)
	go func() { 
    //接收端
		defer wg.Done()
		for { 
   
			var (
				v  int
				ok bool
			)
			select { 
    //随机选择可用channel接收数据
			case v, ok = <-c:
				println("a1:", v)
			case v, ok = <-c:
				println("a2:", v)
			}
			if !ok { 
   
				return //如果任一通道关闭,则终止接收
			}
		}
	}()
	go func() { 
    //发送端
		defer wg.Done()
		defer close(c)
		for i := 0; i < 10; i++ { 
   
			select { 
    //随机选择发送的channel
			case c <- i:
			case c <- i * 10:
			}
		}
	}()
	wg.Wait()
}

a1: 0
a2: 10
a1: 20
a1: 30
a1: 40
a1: 5
a1: 6
a2: 70
a1: 80
a1: 9
a2: 0
38.下面代码输出什么,为什么

package main

func main() { 
   
	done := make(chan struct{ 
   })
	data := []chan int{ 
   
		make(chan int, 3), //数据缓冲区
	}
	go func() { 
   
		defer close(done)//关闭通道,结束阻塞
		for i := 0; i < 10; i++ { 
   
			select { 
   
			case data[len(data)-1] <- i:
			dafault:
				data = append(data, make(chan int, 3))
			}
		}
	}()
	<-done//阻塞
	for i := 0; i < len(data); i++ { 
   
		c := data[i]
		close(c)
		for x := range c { 
   
			println(x)
		}
	}

}

39.下面代码输出什么,为什么
通常使用工厂方法将goroutine和通道绑定

package main

import "sync"

type receiver struct { 
   
	sync.WaitGroup
	data chan int
}

func newReceiver() *receiver { 
   
	r := &receiver{ 
   
		data: make(chan int),
	}
	r.Add(1)
	go func() { 
    //接收端
		defer r.Done()
		for x := range r.data { 
    //接收消息直到通道被关闭
			println("recv:", x)
		}

	}()
	return r
}
func main() { 
   
	r := newReceiver()
	r.data <- 1 //发送端
	r.data <- 2
	close(r.data) //关闭通道,发出结束通知
	r.Wait()      //等待接收者处理结束
}

recv: 1
recv: 2
40.下面代码输出什么,为什么
用通道实现信号量

package main
import (
	"fmt"
	"sync"
	"runtime"
	"time"
)
func main(){ 
   
	runtime.GOMAXPROCS(4)
	var wg sync.WaitGroup
	sem:=make(chan struct{ 
   },2)//最多允许两个并发同时执行
	for i:=0;i<5;i++{ 
   
		wg.Add(1)
		go func(id int){ 
   
			defer wg.Done()
			sem<-struct{ 
   }{ 
   }//acquire:获取信号
			defer func(){ 
   <-sem}()//release:释放信号
			time.Sleep(time.Second*2)
			fmt.Println(id,time.Now().Unix())
		}(i)
	}
	wg.Wait()

}

0 1599985509
4 1599985509
2 1599985511
1 1599985511
3 1599985513
41.下面代码输出什么,为什么
标准库time提供了timeout和tick channel实现

package main
import (
	"fmt"
	"time"
	"os"
)
func main(){ 
   
go func(){ 
   
	for{ 
   
		select{ 
   
		case <-time.After(time.Second*5):
			//func After(d Duration) <-chan Time
			//After会在另一线程经过时间段d后向返回值发送当时的时间。等价于NewTimer(d).C。
			fmt.Println("timeout...")
			os.Exit(0)
			// Exit让当前程序以给出的状态码code退出。一般来说,
			//状态码0表示成功,非0表示出错。程序会立刻终止,defer的函数不会被执行。
		}
	}
}()
go func(){ 
   
	tick:=time.Tick(time.Second)
	for{ 
   
		select{ 
   
		case <-tick:
			fmt.Println(time.Now().Unix())
		}
	}
}()
<-(chan struct{ 
   })(nil)//直接用nil channel阻塞进程

}

42.下面代码输出什么,为什么
通道并非用来取代锁的,它们有各自不同的使用场景。通道倾向于解决逻辑层次的并发处理架构,而锁则用来保护局部范围内的数据安全。

package main

import (
	"sync"
	"time"
)

type data struct { 
   
	sync.Mutex
}

func (d data) test(s string) { 
   
	d.Lock()
	defer d.Unlock()
	for i := 0; i < 5; i++ { 
   
		println(s, i)
		time.Sleep(time.Second)
	}
}
func main() { 
   
	var wg sync.WaitGroup
	wg.Add(2)
	var d data
	go func() { 
   
		defer wg.Done()
		d.test("read")
	}()
	go func() { 
   
		defer wg.Done()
		d.test("write")

	}()
	wg.Wait()
}

write 0
read 0
read 1
write 1
read 2
write 2
read 3
write 3
read 4
write 4

版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 举报,一经查实,本站将立刻删除。

发布者:全栈程序员-用户IM,转载请注明出处:https://javaforall.cn/147963.html原文链接:https://javaforall.cn

【正版授权,激活自己账号】: Jetbrains全家桶Ide使用,1年售后保障,每天仅需1毛

【官方授权 正版激活】: 官方授权 正版激活 支持Jetbrains家族下所有IDE 使用个人JB账号...

(0)


相关推荐

发表回复

您的电子邮箱地址不会被公开。

关注全栈程序员社区公众号