前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >golang面试题(带答案)[通俗易懂]

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

作者头像
全栈程序员站长
发布2022-09-07 13:18:40
1.2K0
发布2022-09-07 13:18:40
举报
文章被收录于专栏:全栈程序员必看

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

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

代码语言:javascript
复制
	//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]
代码语言:javascript
复制
[1 2 3] 哈哈
[1 2 3] 哈哈
[2 3 4]
[2 3 4]

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

代码语言:javascript
复制
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]
}
代码语言:javascript
复制
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.下面代码输出什么,为什么

代码语言:javascript
复制
	//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)
代码语言:javascript
复制
下面是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.下面代码输出什么,为什么

代码语言:javascript
复制
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)
}
代码语言:javascript
复制
哈哈
哈哈
哈哈
哈哈
哈哈
输出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
代码语言:javascript
复制
注:先执行"哈哈",
退出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.下面代码输出什么

代码语言:javascript
复制
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)

}
代码语言:javascript
复制
0 haha
1 haha
2 haha
3 haha
4 haha
5 嗯嗯
5 嗯嗯
5 嗯嗯
5 嗯嗯
5 嗯嗯

6.下面代码输出什么

代码语言:javascript
复制
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)
}
代码语言:javascript
复制
three three three

7.下面代码输出什么

代码语言:javascript
复制
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)
}
代码语言:javascript
复制
three one two
代码语言:javascript
复制
one two three
代码语言:javascript
复制
one three two

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

代码语言:javascript
复制
func main() { 
   

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

	for v := range x { 
   

		fmt.Print(v)

	}

}
代码语言:javascript
复制
012

9.下面代码输出什么

代码语言:javascript
复制
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)

}
代码语言:javascript
复制
132

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

代码语言:javascript
复制
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.下面代码输出什么

代码语言:javascript
复制
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.下面代码输出什么

代码语言:javascript
复制
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.下面代码输出什么

代码语言:javascript
复制
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.下面代码会触发异常吗?请详细说明

代码语言:javascript
复制
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.下面代码有什么问题,如何修改

代码语言:javascript
复制
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 修改后:

代码语言:javascript
复制
func (ua *UserAges) Add(name string, age int) { 
   
	ua.ages = make(map[string]int)
	ua.Lock()
	defer ua.Unlock()
	ua.ages[name] = age
}

输出结果:

代码语言:javascript
复制
map[lisa:24]

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

代码语言:javascript
复制
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.以下代码输出什么?为什么?

代码语言:javascript
复制
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.以下代码输出什么?为什么?

代码语言:javascript
复制
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 修改后:

代码语言:javascript
复制
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.以下代码输出什么?为什么?

代码语言:javascript
复制
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.以下代码输出什么?为什么?

代码语言:javascript
复制
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.以下代码输出什么?为什么?

代码语言:javascript
复制
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.以下代码输出什么?为什么?

代码语言:javascript
复制
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.以下代码输出什么?为什么

代码语言:javascript
复制
package main

var size = 1024
var max_size = size * 2

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

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

代码语言:javascript
复制
var a int
var a = 1
a:=1

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

代码语言:javascript
复制
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.以下代码能输出吗?为什么

代码语言:javascript
复制
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并发计算两组数据的和

代码语言:javascript
复制
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.使用通道并发计算两组数据的和

代码语言:javascript
复制
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.下面代码输出什么,为什么

代码语言:javascript
复制
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.下面代码输出什么,为什么

代码语言:javascript
复制
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.下面代码输出什么,为什么

代码语言:javascript
复制
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.下面代码输出什么,为什么

代码语言:javascript
复制
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.下面代码输出什么,为什么

代码语言:javascript
复制
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语句,他会随机选用一个可用通道做收发操作

代码语言:javascript
复制
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.下面代码输出什么,为什么 通道默认是双向的,并不区分发送和接收端,但某些时候我们可以限制收发操作的方向来获得更严谨的操作逻辑。 通常使用类型转换来获取单向通道,并分别赋予操作双方。

代码语言:javascript
复制
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吗

代码语言:javascript
复制
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选中。

代码语言:javascript
复制
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执行

代码语言:javascript
复制
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.下面代码输出什么,为什么

代码语言:javascript
复制
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和通道绑定

代码语言:javascript
复制
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.下面代码输出什么,为什么 用通道实现信号量

代码语言:javascript
复制
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实现

代码语言:javascript
复制
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.下面代码输出什么,为什么 通道并非用来取代锁的,它们有各自不同的使用场景。通道倾向于解决逻辑层次的并发处理架构,而锁则用来保护局部范围内的数据安全。

代码语言:javascript
复制
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

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

本文参与 腾讯云自媒体同步曝光计划,分享自作者个人站点/博客。
原始发表:2022年6月2,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档