数千道面试题尽在GolangRoadmap,一个年轻的GO开发者社区https://www.golangroadmap.com/,目前是邀请制注册,邀请码:Gopher-1035-0722
感觉百度Go岗位相对C++还是少很多,面试是群面,换了两次面试官才匹配到一个Golang的,然后就被反复摩擦,还是太菜了!
上来简单自我介绍一下,没有问项目,直接开始问Golang
写代码实现两个协程交替打印"1",“2”,这是个比较经典的问题了,务必掌握。
package main
import (
"fmt"
"sync"
)
func main() {
var ch1, ch2 = make(chan struct{}), make(chan struct{})
var wg sync.WaitGroup
wg.Add(2)
go func(s string) {
defer wg.Done()
for i := 1; i <= 10; i++ {
<-ch1
fmt.Println(s)
ch2 <- struct{}{}
}
<-ch1
}("1")
go func(s string) {
defer wg.Done()
for i := 1; i <= 10; i++ {
<-ch2
fmt.Println(s)
ch1 <- struct{}{}
}
}("2")
ch1 <- struct{}{}
wg.Wait()
}
defer与返回值的问题,这个问题主要考察对return语句的理解,"return xxx"包含三个步骤
(1)返回值=xxx
(2)调用defer函数
(3)将返回值返回
当返回值没有声明时,你可以把返回值当作一个Go语言自己声明的变量,如下代码
package main
import "fmt"
func main() {
fmt.Println(f1())//打印0
}
func f1() int {
var a int
defer func() {
a++
}()
//返回值=a,此时a=0
//执行defer,a++,此时a=1
//返回 返回值,返回值=0
return a
}
当返回值提前声明时
package main
import "fmt"
func main() {
fmt.Println(f1())//打印1
}
func f1() (a int) {
defer func() {
a++
}()
//返回值a=a,此时a=0
//执行defer,a++,此时a=1
//返回a
return a//等价与return
}
再看一种情况
package main
import "fmt"
func main() {
fmt.Println(f1())//打印6
}
func f1() (a int) {
defer func() {
a++
}()
//返回值a=5
//执行a++,此时a=6
//返回a
return 5
}
再看一种情况
package main
import "fmt"
func main() {
fmt.Println(f1())//打印5
}
func f1() (y int) {
x:=5
defer func() {
x++
}()
//y=x,此时x=5,y=5
//执行x++,此时x=6,y=5
//返回y
return x
}
最后一种情况
package main
import "fmt"
func main() {
fmt.Println(f1())//打印6
}
func f1() (x int) {
defer func(x int) {
x++
}(x)
//x=5
//x++
//返回x
return 5
}
如果优雅关闭http服务器
http-server运行过程中,若进程被关闭,那么正在处理的请求可能只被处理了一半就停止了,可能会产生数据的不一致。
优雅关机是指:首先,停止接收新请求;然后,等待队列中的请求被处理完毕;最后,应用程序退出;
Golang http.Server 结构体有一个终止服务的方法 Shutdown,使用 Shutdown 可以优雅的终止服务,其不会中断活跃连接。其工作过程为:首先关闭所有开启的监听器,然后关闭所有闲置连接,最后等待活跃的连接均闲置了才终止服务。若传入的 context 在服务完成终止前已超时,则 Shutdown 方法返回 context 的错误,否则返回任何由关闭服务监听器所引起的错误。当 Shutdown 方法被调用时,Serve、ListenAndServe 及 ListenAndServeTLS 方法会立刻返回 ErrServerClosed 错误。请确保 Shutdown 未返回时,勿退出程序。
signal 包的 Notify 函数提供系统信号通知的能力
func Notify(c chan<- os.Signal, sig …os.Signal)
参数 c 是调用者的信号接收通道,Notify 可将进入的信号转到 c。sig 参数为需要转发的信号类型,若不指定,所有进入的信号都将会转到 c。信号不会阻塞式的发给 c:调用者需确保 c 有足够的缓冲空间,以应对指定信号的高频发送。对于用于通知仅一个信号值的通道,缓冲大小为 1 即可。有了signal.Notify,传入一个 chan 并指定中断参数,这样当系统中断时,即可接收到信号。参看如下代码,当使用 Ctrl+C 时,c 会接收到中断信号,程序会在打印“program interrupted”语句后退出。
func main() {
c := make(chan os.Signal)
signal.Notify(c, os.Interrupt)
<-c
log.Fatal("program interrupted")
}
接下来我们使用如上 signal.Notify 结合 http.Server 的 Shutdown 方法实现服务优雅的终止。如下代码,Handler其会在2s后返回 hello。创建一个 http.Server 实例,指定端口与 Handler。声明一个 processed chan,其用来保证服务优雅的终止后再退出主 goroutine。新启一个 goroutine,其会监听 os.Interrupt 信号,一旦服务被中断即调用服务的 Shutdown 方法,确保活跃连接的正常返回(本代码使用的 Context 超时时间为 3s,大于服务 Handler 的处理时间,所以不会超时)。处理完成后,关闭 processed 通道,最后主 goroutine 退出。
var addr = flag.String("server addr", ":8080", "server address")
func main() {
// handler
handler := http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
time.Sleep(2 * time.Second)
fmt.Fprintln(w, "hello")
})
// server
srv := http.Server{
Addr: *addr,
Handler: handler,
}
// make sure idle connections returned
processed := make(chan struct{})
go func() {
c := make(chan os.Signal, 1)
signal.Notify(c, os.Interrupt)
<-c
ctx, cancel := context.WithTimeout(context.Background(), 3*time.Second)
defer cancel()
if err := srv.Shutdown(ctx); nil != err {
log.Fatalf("server shutdown failed, err: %v\n", err)
}
log.Println("server gracefully shutdown")
close(processed)
}()
// serve
err := srv.ListenAndServe()
if http.ErrServerClosed != err {
log.Fatalf("server not gracefully shutdown, err :%v\n", err)
}
// waiting for goroutine above processed
<-processed
}
欢迎分享,转载请注明来源:内存溢出
评论列表(0条)