大家好,又见面了,我是你们的朋友全栈君。
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账号...